VPS · How to
Deploy a website (Astro or Next.js) on a VPS with nginx and HTTPS
To publish a website on an Ubuntu VPS you need four pieces: nginx to serve it, your project built into a folder, a free Let's Encrypt HTTPS certificate and, if it is an application with a server such as Next.js, a systemd service to keep it alive. It is what we use for this site and 20 others; these are the steps.
Updated 2026-11-10 · By Rómulo Felizola · Leer en español
Before you start
- A VPS with Ubuntu 24.04 already prepared with the first-day checklist.
- A domain with an A record pointing to the VPS's IP (DNS changes can take time to propagate).
- Your project built: dist/ for Astro or the standalone output for Next.js.
apt install nginx certbot python3-certbot-nginx -yCase A: static site (Astro, HTML)
Copy the built folder to the server and serve it with nginx. Ours looks like this, simplified:
server {
server_name yourdomain.com;
root /srv/yourdomain/site;
index index.html;
server_tokens off;
location /_astro/ {
add_header Cache-Control "public, max-age=31536000, immutable" always;
try_files $uri =404;
}
}The files in /_astro/ have a name with a content fingerprint, so they can be cached for a year safely. Save the file in /etc/nginx/sites-available/, link it in sites-enabled and reload:
ln -s /etc/nginx/sites-available/yourdomain.conf /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginxCase B: Next.js application (or any app with a server)
The application runs as a service, listens only on localhost and nginx proxies to it. The service (ours, with a memory cap):
# /etc/systemd/system/myapp.service
[Service]
User=myapp
WorkingDirectory=/srv/myapp/.next/standalone
Environment="NODE_ENV=production"
Environment="PORT=3020"
Environment="HOSTNAME=127.0.0.1"
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5
MemoryMax=400M
[Install]
WantedBy=multi-user.targetsystemctl daemon-reload
systemctl enable --now myappAnd in nginx, the block that forwards the traffic:
location / {
proxy_pass http://127.0.0.1:3020;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}Free HTTPS with Let's Encrypt
certbot --nginx -d yourdomain.com -d www.yourdomain.com
certbot renew --dry-runcertbot edits your nginx configuration and enables a timer that renews certificates before they expire. The second command checks the renewal will work: always do it, because an expired certificate is a site the browser marks as dangerous.
What to check when you finish
- curl -I https://yourdomain.com returns 200 and the header with your certificate.
- systemctl status myapp shows active, and after a server reboot it is still active.
- If something fails with a 502, follow the 502 guide.
To have somewhere to try it:KVM 2 VPS at Hostinger
If you'd rather have someone set it up and maintain it, that is what our website maintenance (in Spanish) is for.
Frequently asked questions
How many websites fit on one VPS?
With nginx you can serve each domain with its own configuration file. The limit is the memory your applications use: see the measurement on our server.
Do I need a control panel?
No. Hostinger's page does not show one as included by default, although it offers optional templates with panels such as cPanel, Plesk or CloudPanel. On our server we use nginx and systemd with no panel.
Does the certificate renew by itself?
Yes, certbot installs a renewal timer. Check it with certbot renew --dry-run and with systemctl list-timers.