Why static export + cPanel
Not everyone has the budget or inclination to run a Node.js process on a server.
cPanel shared hosting is cheap, reliable, and already paid for by a lot of people.
With output: 'export', Next.js produces a folder of static HTML, CSS, and JS
that cPanel can serve with no extra configuration.
The trade-offs are real: no server-side rendering at request time, no API routes that require a running server, no streaming. For a portfolio and blog, none of that matters.
The pipeline
- name: Deploy via FTP
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
with:
server: ${{ secrets.FTP_SERVER }}
username: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
local-dir: ./out/
server-dir: ${{ secrets.FTP_SERVER_DIR }}
That's it. The action does a diff on every deploy so it only uploads changed files. Important when you're on shared hosting with FTP rate limits.
The gotchas
Trailing slashes
Set trailingSlash: true in next.config.ts or cPanel will return
404s for routes like /writing instead of serving writing/index.html.
Image optimisation
Set images: { unoptimized: true }. The Next.js image optimisation
pipeline requires a running server, so it has no place in a static
export.
dangerous-clean-slate
Start with it set to true on first deploy to clear any leftover
files, then set it back to false. If you leave it true, every
deploy wipes the whole directory before uploading. Fine in theory,
but it means a brief window where your site is empty.
More detail coming in a follow-up post.