Fitting a Next.js app into a 500 MB cPanel quota
Build off the server, ship only what runs, and keep the data where a redeploy cannot touch it.
The hosting account is a Safaricom Cloud cPanel Bronze package: 1 GB of RAM, one CPU core, a 500 MB disk and 20 entry processes. Node 24 and Python 3.11 are available through cPanel’s app selectors. The database is SQLite, because the plan has no PostgreSQL and the filesystem is persistent.
The 500 MB is the binding constraint, and it is shared by the app, the database and every uploaded image. A normal npm install on the server would not fit. This project’s node_modules alone exceeds the quota.
Build somewhere else
So the frontend is built off the server, on my machine, into Next’s standalone output, which bundles only the modules the app actually uses. One command produces both bundles:
SITE_URL=https://chamachamiminawewe.co.ke ./scripts/build-cpanel.shThe script runs the Next build, then assembles what the server needs and nothing else:
- The standalone server from
.next/standalone. .next/staticandpublic/, copied in explicitly, because the standalone server does not include them.- The backend: the FastAPI app, its WSGI entry file and a trimmed requirements file.
- Two tar archives, ready for cPanel’s File Manager.
The numbers
The frontend bundle is about 37 MB (9 MB compressed). The backend is about 53 KB plus a roughly 60 MB Python virtualenv. That leaves around 380 MB for the database and uploads. Images, not data, are what will eat that space.
Why SITE_URL is set at build time
robots.txt, canonical URLs and the Open Graph tags on prerendered pages are generated during the build. Change the domain later and the pages are wrong until you rebuild.
Where things live on the server
- The frontend and backend each sit in their own folder in the home directory, not in
public_html. Passenger serves them, and anything inpublic_htmlis directly downloadable. - The database and uploads sit in a third folder, outside both apps, so a redeploy never touches them.
- Uploads are served same-origin under
/api, which means no CORS and no internal hostname leaking into the page.
Do not run npm install on the server. The bundle already contains everything it needs, and installing would blow the quota. To redeploy, extract the new archive over the frontend folder and restart the Node app.
What to watch on this plan
- Disk. The real limit, and uploaded images are the growth risk.
- Entry processes (20). A traffic spike can hit this ceiling and requests then queue or return 508. Worth monitoring during a campaign push.
- SQLite write concurrency. Fine at this plan’s traffic ceiling. If the site outgrows it, MySQL is available with its own separate quota of about 499 MB.
The point
A hard limit is a specification. Once the 500 MB is written down as a budget, with a line for each item, the design decisions mostly make themselves.
Written from the deployment notes of Chama Cha Mimi na Wewe. The two Python-selector faults it mentions have their own write-up: the cPanel Python selector leaves your app broken in two silent ways.