Next.js Auto-Deployment to a VPS Using GitHub Actions
12/19/2025
A practical, no-hand-waving guide to doing CI/CD correctly
Modern web deployments fail for one reason more than any other: people do not understand where code runs versus where code is built.
This article walks through a clean, correct, and production-grade way to automatically deploy a Next.js application to a VPS using GitHub Actions. It avoids common myths, cargo-cult tutorials, and ambiguous language like “your local machine” that cause real operational mistakes.
By the end, you will understand not just how to deploy, but why this model works.
The Core Problem This Solves
Most broken deployment setups share the same issues:
- Builds running directly on the VPS
- Manual SSH sessions for deployment
- Production servers overloaded during builds
- Confusion over SSH keys and access control
- “Works on my machine” deployment scripts
GitHub Actions exists to remove these problems entirely.
The key is understanding separation of responsibility.
The Three Machines Involved
There are three distinct systems involved in a modern CI/CD pipeline. Treating them as interchangeable is a mistake.
1. Your Development Machine
This is your laptop or workstation.
Its responsibilities:
- Write code
- Commit code
- Push code to GitHub
That is all.
It does not deploy.
It does not connect to production.
Once you push, your job is done.
2. The GitHub Actions Runner
This is a short-lived virtual machine created by GitHub.
Its responsibilities:
- Check out your repository
- Install dependencies
- Build the Next.js application
- Authenticate to your VPS
- Transfer build artifacts
This runner is not your server.
It is destroyed after every run.
This is intentional. Builds are disposable.
3. Your VPS (The Only Production System)
This is the machine that actually matters.
Its responsibilities:
- Serve traffic
- Run Node.js
- Run PM2
- Host nginx
- Stay stable
A production server should be boring.
It should not compile JavaScript or resolve npm dependencies on every deploy.
The Deployment Philosophy
The correct deployment flow looks like this:
- You push code to
main - GitHub Actions builds the app elsewhere
- Only the build output is sent to the VPS
- The running process is restarted cleanly
This design:
- Keeps production stable
- Reduces downtime
- Prevents resource spikes
- Makes deployments repeatable
This is CI/CD as it was intended to be used.
Preparing the VPS
All server-side setup happens once.
You install:
- Node.js
- PM2
- nginx (optional but recommended)
You clone the repository one time to establish the directory structure, install dependencies, and verify the app runs. After that, the VPS stops caring about GitHub entirely. It does not pull code. It does not build.
It simply runs.
SSH Access: Who Connects to What
This is where many tutorials go wrong.
You are not giving your laptop access to the VPS.
You are giving GitHub Actions access to the VPS.
That means:
- The SSH key exists for automation
- The private key is stored in GitHub Secrets
- The public key lives on the VPS
- The VPS decides who is allowed in
This is a deploy key, not a developer key.
Once configured, GitHub Actions can connect without human involvement.
The GitHub Actions Workflow
The workflow file defines the entire deployment lifecycle in a reproducible way.
It does the following, in order:
- Spins up a fresh runner
- Checks out your repository
- Installs Node and dependencies
- Builds the Next.js app
- Injects an SSH key from secrets
- Syncs build artifacts to the VPS
- Restarts the running process
Every step is logged. Every run is auditable.
If deployment fails, nothing touches production.
Why Artifacts, Not git pull
A common anti-pattern is SSHing into the server and running git pull.
This couples your production environment to:
- GitHub availability
- Branch state
- Dependency resolution
- Build tooling
Artifact-based deployment avoids all of this.
The VPS receives only what it needs to run.
No source control logic. No dependency churn.
This is safer, faster, and easier to reason about.
Operational Benefits
Once set up, this approach gives you:
- One-command deployments (
git push) - Zero manual SSH sessions
- Predictable rollouts
- Clear failure modes
- Easy rollback (re-deploy a previous build)
Most importantly, it scales cleanly as your app grows.
Common Mistakes to Avoid
- Building on the VPS
- Using your personal SSH key for automation
- Deploying directly from your laptop
- Mixing build and runtime responsibilities
- Treating GitHub Actions as “just scripts”
CI/CD is infrastructure. Treat it accordingly.
Final Thoughts
A production deployment pipeline should be:
- Boring
- Predictable
- Automated
- Repeatable
If deploying your app feels stressful or fragile, the problem is not Next.js. It is the deployment model.
GitHub Actions plus a properly configured VPS solves this cleanly when you respect the separation between development, build, and runtime.
Once you internalize that separation, everything else becomes straightforward.