Pull Request Preview Deploys for Static Sites on a Cheap VPS

TL;DR / Summary: Pull-request preview sites do not require a pricey PaaS. A cheap VPS with nginx, a deploy user, and per-branch directories gives designers and clients a URL for every open PR while production stays untouched on its own hostname.

The preview problem small teams actually have

Static site generators make production deploys easy and preview deploys awkward. Hosting platforms solve previews with magic URLs and a monthly bill that grows with seats. A five-person docs or marketing team often only needs disposable subdomains that die when the pull request merges.

A budget VPS can host those previews if you accept a little scripting. The payoff is predictable cost and no surprise charge when a contractor opens twelve branches in a week.

This pattern fits HTML, docs sites, and front-end builds that compile to a folder of static files. It is a poor fit for apps that need a database per preview.

One server, two jobs, clear walls

Run production on www or the bare domain. Run previews on a separate hostname such as preview.example.com with path-based or subdomain-based routing. Never deploy experimental branches into the production document root “temporarily.” Temporary becomes permanent the week everyone is busy.

  • Deploy user with rsync rights — CI logs in as deploy, not as root
  • Per-PR directory — /var/www/previews/pr-184/ keeps cleanup obvious
  • Automatic TLS — a wildcard cert or a per-PR cert strategy decided up front
  • Disk quota mindset — old previews must expire or the SSD fills with forgotten zips

When you size the box, look at concurrent preview count more than peak production traffic. Browse options on affordable VPS server pages for 2 GB plans if your builds unpack large asset folders; 1 GB is often enough for docs-only sites.

A minimal CI handshake

On each pull request, CI builds the static output, rsyncs it into the PR directory, and comments the URL back on the PR. On close or merge, CI deletes that directory. If CI cannot delete, a nightly cron that removes previews older than seven days is the safety net.

Auth for private work

Public previews are fine for open marketing pages. Private product docs need basic auth or an SSO gate at the reverse proxy. Putting secrecy only in an obscure URL is not access control; it is optimism.

If the preview URL leaks, the protection should still be a password or SSO—not hope.

nginx layout that stays readable

Prefer one server block for previews that maps /pr/184/ to the matching directory, or a subdomain scheme like 184.preview.example.com if you already have wildcard DNS. Path-based routing is simpler for teams new to DNS wildcards.

Disable directory listing. Enable gzip or brotli for text assets. Set short cache headers on previews so designers are not fighting yesterday’s CSS after a push.

Keep access logs rotated. Preview traffic is chatty during review week and will fill a disk if logrotate is forgotten.

Build resource limits on small CPUs

Static builds that run webpack-style pipelines can spike CPU. On a one-core VPS, two simultaneous PR builds will make SSH feel sticky. Limit CI concurrency to one preview build at a time or move the build step to CI runners and only upload artifacts to the VPS.

Artifact-only deploys are usually the right default: CI builds elsewhere, the VPS only receives files and reloads nothing heavier than nginx. That keeps the cheap server honest about its job.

Client review habits that reduce rework

Paste the preview link in the same chat thread as the design notes. Ask for comments tied to the PR number. When feedback arrives as “the staging site,” nobody knows which branch they meant.

Destroy previews promptly after merge so nobody bookmarks a zombie URL and files bugs against deleted experiments.

DNS and certificate gotchas on first setup

Point the preview hostname at the VPS before you expect CI to succeed. Certificate issuance fails in confusing ways when DNS still targets an old host. If you use path-based previews on one hostname, one certificate covers every PR. If you use per-PR subdomains, arrange wildcard DNS and a wildcard certificate up front or you will spend a week issuing toys.

Document the DNS records in the same repo as the deploy script. Future teammates should not rediscover the preview host by grepping Slack.

Protecting production from preview accidents

Separate SSH keys for preview deploys and production deploys. CI for pull requests should not hold the production key. A mistaken rsync destination is rarer when the credential literally cannot write to production paths.

Add a dry-run mode to the deploy script that prints the target directory and exits. Require dry-run output in the first PR that introduces the pipeline. Five minutes of theater prevents a memorable outage.

Label preview pages in the HTML title with “PREVIEW” and the PR number. Stakeholders sometimes bookmark freely; the label reduces angry tickets about “prod looking weird” when they were on a branch the whole time.

What success looks like after a month

You should see a stable monthly VPS invoice, a trash cron that keeps disk free, and PR comments that always include a fresh link. Designers stop asking for “a quick upload somewhere.” Production deploys stay boring. That boredom is the feature: a cheap VPS earning its keep as a preview factory, not as an accidental second production.

Affordable-Vps-Server-Author
Affordable-Vps-Server-Author
Articles: 395