For a long time, I relied exclusively on Vercel for my Next.js projects. The developer experience is unmatched, but as my traffic grew, so did the ‘bandwidth anxiety.’ If you’ve ever looked at a surprise bill from a serverless platform, you know exactly what I mean. That’s why I started looking for a more predictable, cost-effective alternative. In this guide, I’ll show you how to deploy Next.js on Hetzner, which offers a fantastic balance of raw performance and aggressive pricing.
Before we dive in, it’s worth noting that while Vercel is great for hobbyists, moving to a VPS (Virtual Private Server) gives you full control over your environment. If you’re curious about how Hetzner stacks up against other providers, check out my Hetzner Cloud review for developers.
Prerequisites
Before we start the deployment process, make sure you have the following ready:
- A Hetzner Cloud account with a project created.
- A domain name pointed to your Hetzner server IP.
- A Next.js project pushed to a GitHub repository.
- Basic familiarity with the Linux terminal (SSH).
Step 1: Provisioning your Hetzner Server
I recommend starting with the CX21 or CX31 instance for Next.js. While the smallest plan works, Next.js builds can be memory-intensive and might crash on 2GB RAM instances without a swap file.
- Log into the Hetzner Cloud Console.
- Click Create Server.
- Select Ubuntu 22.04 LTS as your image.
- Choose the location closest to your users (e.g., Falkenstein or Helsinki).
- Add your SSH key (avoid using passwords for security).
- Click Create & Buy Now.
Step 2: Preparing the Server Environment
Once you’ve SSH’d into your server (ssh root@your_server_ip), we need to install the necessary runtime. I prefer using NVM (Node Version Manager) to handle Node.js versions.
# Update system packages
sudo apt update && sudo apt upgrade -y
# Install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
. ~/.nvm/nvm.sh
# Install the latest LTS version of Node
nvm install --lts
# Install PM2 to keep your app running in the background
npm install -g pm2
Step 3: Deploying the Next.js Application
Now, let’s pull your code and get it running. I’ll use a simple Git-based deployment here, though for larger teams, you might consider a CI/CD pipeline. If you’re managing multiple apps, you might find deploying a monorepo more complex, but the principle of server-side build remains the same.
# Clone your repository
git clone https://github.com/yourusername/your-nextjs-app.git
cd your-nextjs-app
# Install dependencies
npm install
# Create your .env file
nano .env.local
# (Add your environment variables here, then Ctrl+O, Enter, Ctrl+X)
# Build the application
npm run build
Now we use PM2 to start the application. PM2 ensures that if the app crashes or the server reboots, your site comes back online automatically.
pm2 start npm --name "next-app" -- start
pm2 save
pm2 startup
Step 4: Setting up Nginx as a Reverse Proxy
By default, Next.js runs on port 3000. We don’t want users typing yourdomain.com:3000. We’ll use Nginx to route traffic from port 80 (HTTP) and 443 (HTTPS) to port 3000.
# Install Nginx
sudo apt install nginx -y
# Create a config file for your site
sudo nano /etc/nginx/sites-available/next-app
Paste the following configuration (replace yourdomain.com with your actual domain):
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Enable the config and restart Nginx:
sudo ln -s /etc/nginx/sites-available/next-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
Step 5: Enabling SSL with Let’s Encrypt
Security is non-negotiable. Use Certbot to get a free SSL certificate from Let’s Encrypt.
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Follow the prompts, and Certbot will automatically update your Nginx config to redirect all HTTP traffic to HTTPS.
Pro Tips for Production
- Swap Space: If you are on a 2GB RAM plan, create a 2GB swap file. Next.js builds can easily spike and trigger an “Out of Memory” error.
- CI/CD: Use GitHub Actions to SSH into your server and run
git pull && npm install && npm run build && pm2 restart next-appon every push to main. - Monitoring: Use
pm2 monitto see real-time CPU and memory usage of your Next.js process.
Troubleshooting Common Issues
The build fails with “JavaScript heap out of memory”
This is common on small VPS instances. Increase the Node memory limit during build:
NODE_OPTIONS="--max-old-space-size=2048" npm run build
Nginx shows “502 Bad Gateway”
This usually means your Next.js app isn’t running on port 3000. Check your PM2 status with pm2 list and ensure the app is “online”.
What’s Next?
Now that you’ve mastered how to deploy Next.js on Hetzner, you might be wondering about other hosting options. If you’re comparing PaaS options, my guide on Railway.app vs Vercel explores the middle ground between a raw VPS and a fully managed platform.
Ready to scale? Consider setting up a separate database server on Hetzner to keep your application and data decoupled.