How to Run a Staging Environment on One VPS
You do not need a second server for staging. Here is how to run a staging environment on the same VPS as production without them colliding.
You can run a staging environment on the same VPS as production without the two stepping on each other, and for a small product that is the right call. You do not need a second server, a second provider, or a managed preview platform. You need process isolation, a separate database, and a subdomain. I run staging alongside production on the same boxes across my portfolio. It costs nothing extra and it catches the bugs that only show up outside your laptop.
Can staging and production share a server?
Yes, as long as they cannot touch each other's data or ports. The two rules that matter: staging gets its own database, and staging gets its own set of containers or processes. Share the machine, never the state. If staging can write to the production database, it is not staging, it is a loaded gun pointed at your customers.
The clean way to do this is a second Docker Compose project on the same box. Give the staging stack its own project name, its own database container with its own volume, its own env file, and its own internal network. Now staging and production run side by side, isolated by Docker's networking, sharing only the hardware. A bad migration in staging cannot reach production data because they are literally different database processes with different volumes. This is the same isolation discipline I apply when deciding whether the database shares the app's box.
Route staging on a subdomain
Put a reverse proxy in front and route by hostname. app.example.com goes to the production web container, staging.example.com goes to the staging one. Caddy or nginx handles this in a few lines, and you get automatic HTTPS for both. If you already run one reverse proxy for every app on the box, staging is just another upstream in the same config.
Lock staging down. It should not be crawlable and it should not be public. Put basic auth in front of it at the proxy, or restrict it to your IP, or both. Staging environments leak: half-built features, real-looking test data, debug endpoints. Treat the front door with the same care you give production, covered in putting Cloudflare in front of your own server.
Keep staging and production in parity
Staging is only useful if it resembles production. The point is to catch what your laptop hides: real environment variables, real reverse proxy behavior, real TLS, the actual container build. Run the identical image you will ship to production, just pointed at the staging database and config. If staging runs a different build than prod, it is testing something you will never deploy.
Where staging should differ is data and scale. Do not copy live customer data into staging without scrubbing it. Seed it with representative fake data, or import a sanitized snapshot. And staging can run on smaller resource limits since it serves one person. Set memory caps on the staging containers so a runaway process there cannot starve production. The machine is shared, so the resource boundaries matter. Size the box with both environments in mind, which I cover in how to size a VPS for your workload.
Deploy to staging first, then promote
The workflow that makes this pay off: deploy every change to staging, look at it running, then promote the exact same build to production. With a git-push flow you can wire a staging branch to the staging stack and main to production, so pushing to staging rebuilds only that environment. I walk through the mechanics in git-push deploy on your own box.
Promotion should be boring. Because staging and production run the same image, promoting is retagging or re-pointing production at the build you already validated. No surprises, because the thing you tested is the thing you ship. That is the entire reason staging exists, and running it on the same HostSSH box you already pay for removes the last excuse not to have one. A second environment used to mean a second bill. On one VPS it means a second Compose project and a subdomain, and it will save you from shipping a broken migration to real users.