Deploy to VPS
What Is a VPS?
Section titled “What Is a VPS?”A Virtual Private Server (VPS) is a virtual machine rented from a hosting provider. You get root access, full control over the operating system, and the ability to install any software you need.
For Astro Docus, a VPS is a powerful choice because:
- Full control — you configure everything exactly how you want it
- Cost-effective — a $4–6/month VPS can serve thousands of requests per second
- No vendor lock-in — your site is just files on your own server
- Scalable — add more resources as your traffic grows
The trade-off is complexity. You manage the server, the web server, and the SSL certificates yourself. This guide walks through every step.
Prerequisites
Section titled “Prerequisites”Before starting, you need:
- A VPS from any provider (DigitalOcean, Linode, Vultr, Hetzner, etc.)
- A domain name pointed to your VPS IP address
- SSH access to your server (root or sudo user)
- Your Astro Docus project built locally (
npm run buildworks)
Architecture Overview
Section titled “Architecture Overview”User → Nginx (port 443) → Static files in /var/www/your-domain/dist/- Nginx serves your static files directly. No Node.js runtime needed.
- Let’s Encrypt provides free SSL certificates.
- Certbot automates certificate installation and renewal.
This is the standard architecture for static sites on a VPS. Nginx is fast, reliable, and uses minimal resources .
Step 1 — Connect to Your VPS
Section titled “Step 1 — Connect to Your VPS”Open your terminal and connect via SSH:
ssh root@YOUR_VPS_IPReplace YOUR_VPS_IP with your server’s public IP address. On Windows, use PowerShell or an SSH client like PuTTY .
Once connected, update the package list:
apt update && apt upgrade -yStep 2 — Install Node.js
Section titled “Step 2 — Install Node.js”Astro requires a modern Node.js version. Ubuntu’s default repository often has an older version, so install from NodeSource :
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -apt install -y nodejsVerify the installation:
node -v # should show v22.x.xnpm -vIf node -v shows a version below 22, rerun the NodeSource script as root .
Step 3 — Install Nginx
Section titled “Step 3 — Install Nginx”apt install -y nginxsystemctl enable nginxsystemctl start nginxVerify Nginx is running:
curl http://localhostYou should see the default Nginx welcome page HTML .
Step 4 — Configure the Firewall
Section titled “Step 4 — Configure the Firewall”Install UFW (Uncomplicated Firewall) and open the necessary ports:
apt install -y ufwufw allow OpenSSHufw allow 80/tcpufw allow 443/tcpufw enableCheck the rules:
ufw statusExpected output:
OpenSSH ALLOW Anywhere80/tcp ALLOW Anywhere443/tcp ALLOW AnywherePort 80 is needed for Let’s Encrypt validation. Port 443 is for HTTPS traffic. SSH must remain open or you will lock yourself out .
Step 5 — Upload Your Project to the Server
Section titled “Step 5 — Upload Your Project to the Server”There are two ways to get your Astro Docus project onto the server.
Option A — Git Clone (Recommended)
Section titled “Option A — Git Clone (Recommended)”If your project is on GitHub:
cd /var/wwwgit clone https://github.com/your-username/astrodocus.gitcd astrodocusnpm installOption B — Rsync from Local
Section titled “Option B — Rsync from Local”From your local machine (not the server):
rsync -avz ./your-astro-project root@YOUR_VPS_IP:/var/www/astrodocusThen on the server:
cd /var/www/astrodocusnpm installStep 6 — Build the Static Site
Section titled “Step 6 — Build the Static Site”npm run buildThis generates the dist/ folder containing your static HTML, CSS, and JavaScript files .
Verify the build:
ls dist/You should see index.html, _astro/, and other files.
Step 7 — Configure Nginx for Static Serving
Section titled “Step 7 — Configure Nginx for Static Serving”Create a new Nginx configuration file:
nano /etc/nginx/sites-available/astrodocusPaste this configuration :
server { listen 80; listen [::]:80; server_name your-domain.com www.your-domain.com;
root /var/www/astrodocus/dist; index index.html;
# Gzip compression gzip on; gzip_types text/plain text/css application/json application/javascript text/xml; gzip_min_length 256;
# Cache hashed assets aggressively location /_astro/ { expires 1y; add_header Cache-Control "public, immutable"; }
# Cache other static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 30d; add_header Cache-Control "public, no-transform"; }
# Handle routing location / { try_files $uri $uri/ =404; }
# Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always;}Replace your-domain.com with your actual domain, and /var/www/astrodocus/dist with the path to your dist folder.
Why
try_files $uri $uri/ =404? For static sites with multiple HTML files, this serves the exact file requested. Unlike SPA fallback (/index.html), it returns a 404 for missing pages instead of silently serving the homepage .
Enable the site and test:
ln -s /etc/nginx/sites-available/astrodocus /etc/nginx/sites-enabled/nginx -tsystemctl reload nginxYour site should now be accessible via HTTP at your domain.
Step 8 — Install SSL with Let’s Encrypt
Section titled “Step 8 — Install SSL with Let’s Encrypt”Install Certbot and the Nginx plugin :
apt install -y certbot python3-certbot-nginxObtain a certificate:
certbot --nginx -d your-domain.com -d www.your-domain.comCertbot will ask for your email address and whether to redirect HTTP to HTTPS. Choose Redirect (option 2) for automatic HTTPS .
After completion, Certbot automatically modifies your Nginx configuration to use SSL and sets up HTTP-to-HTTPS redirection.
Verify:
curl -I https://your-domain.comYou should see HTTP/2 200 and a valid SSL certificate.
Step 9 — Verify Automatic Renewal
Section titled “Step 9 — Verify Automatic Renewal”Let’s Encrypt certificates are valid for 90 days. Certbot sets up automatic renewal via a systemd timer :
systemctl status certbot.timerTest the renewal process:
certbot renew --dry-runIf no errors appear, renewal is working correctly.
Important: Keep port 80 open permanently. Let’s Encrypt validates your domain over HTTP on every renewal. Closing port 80 will cause certificates to expire silently .
Automating Deployments (Optional)
Section titled “Automating Deployments (Optional)”Once the server is set up, you can automate deployments so every push to main updates the live site.
Option A — GitHub Actions with Self-Hosted Runner
Section titled “Option A — GitHub Actions with Self-Hosted Runner”Run a GitHub Actions runner on your VPS. The workflow builds Astro and rsyncs dist/ to the web root :
name: Deploy to VPS
on: push: branches: [main]
jobs: build-and-deploy: runs-on: self-hosted steps: - uses: actions/checkout@v4
- uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm'
- run: npm ci - run: npm run build
- run: | rsync -av --delete dist/ /var/www/astrodocus/dist/Option B — Webhook Server
Section titled “Option B — Webhook Server”Run a small Node.js webhook listener on the VPS. When GitHub pushes, the webhook pulls the latest code, rebuilds, and updates the Nginx site . This requires additional setup but avoids a self-hosted runner.
Troubleshooting
Section titled “Troubleshooting”| Problem | Likely Cause | Fix |
|---|---|---|
| 403 Forbidden | File permissions | Ensure Nginx user can read dist/ files |
| 502 Bad Gateway | Nginx proxying to a stopped service | Remove any proxy_pass — serve files directly |
| SSL certificate fails | Port 80 blocked | Open port 80 in UFW and any cloud firewall |
| DNS not resolving | A record missing | Add an A record pointing to your VPS IP |
| Changes not showing | Browser cache | Hard refresh (Ctrl+Shift+R) |
| Build fails on server | Node version mismatch | Verify node -v shows v22+ |
Quick Recap
Section titled “Quick Recap”1. SSH into VPS: ssh root@YOUR_VPS_IP2. apt update && apt upgrade -y3. Install Node.js 22 from NodeSource4. apt install -y nginx && systemctl start nginx5. Configure UFW: allow 22, 80, 4436. Upload project: git clone or rsync7. npm install && npm run build8. Create Nginx config in /etc/nginx/sites-available/9. Enable site and reload Nginx10. Install Certbot: apt install certbot python3-certbot-nginx11. Obtain SSL: certbot --nginx -d your-domain.com12. Verify renewal: certbot renew --dry-runA VPS gives you full control at low cost. Nginx serves static files efficiently, Certbot handles SSL automatically, and the total monthly cost is typically $4–6 for a modest server .
👉 Next: docs/deploy-hosting/cpanel-plesk.md — deploy to cPanel or Plesk shared hosting.