deploy · tutorial · vps
Localhost to VPS in One Command? Almost
The last mile from laptop to server: a one-time server setup, a settings sync and a deploy that waits for a health check.

"Works on my machine" ships nowhere
The app sings on localhost.
Hot reload.
Instant feedback.
All green.
Then deployment Saturday arrives, and the weekend disappears.
Server setup.
Settings typos.
A database that exists only as a file on the laptop.
Monday's standup: "deployment in progress." Translation: the app works in exactly one place on Earth, and it is the laptop in the backpack.
The last mile is solved.
Develop recklessly local.
Ship boringly remote.
Compress the gap to one setup and one command.
The gap, itemized
| Gap | Localhost | Server reality | What bridges it |
|---|---|---|---|
| Settings | A casual local file | Exact production values | One sync command |
| Build | Your laptop's chips | A Linux server | Build once, swap in the result |
| Database | Dev data and seeds | Real customer data | Separate files, same shape |
| Process life | A dev server you watch | An app that restarts itself | Set up once by the kit |
| Hostname and ports | localhost:3000 | A domain, no open ports | A tunnel and a private network |
Three details eat the naive weekend.
Transcribed secrets drift on first contact. Retype twelve settings into a server editor and one has a typo. Usually the payment key, found at the first real charge.
Hand-built servers become snowflakes. A box configured from memory is a box nobody can rebuild. A script builds the same server every time, so a dead one is replaceable.
Dev data must never board. Seed users, test orders and fake keys would pollute your production books if the file were copied over. Keep separate files. Move data on purpose.
What to do
Set the server up once.
Run the one-shot setup from your laptop against a fresh Ubuntu server. It creates a non-root user and adds swap space so nothing runs out of memory. It installs the runtime and the web server, closes the firewall to everything but what the app needs, and makes the app restart after a reboot.
It also installs the scheduled housekeeping jobs.
Fresh boxes converge to the same shape. No "this server is special" lore.
Then, every time, sync settings, then deploy.
Run bp secret sync first. The server should hold the production truth before new code arrives.
Then bp deploy.
It checks your code, builds on your laptop, ships the result and swaps it in, with the previous version kept beside it.
It waits for a health check.
If the new version never answers, the old one is put back.
Last, verify from the outside.
Open the public address in a browser, not localhost. Place a test order. Run bp status to confirm the branch, the database size and your latest backup.
That proves the whole chain: DNS, edge, app and database.
First-deploy scorecard
| Check | The naive Saturday | The last-mile setup |
|---|---|---|
| Time to live | A long day | The setup plus a short loop |
| Settings typos | Likely | Copied from a file |
| Dev data in prod | Possible | Separate files |
| The second deploy | Another Saturday? | One command |
The setup script is a commitment device. Manual tweaks outside it drift and confuse the next deploy.
Treat servers as replaceable. Built by the script. Rebuilt by it.
Know this first
One command still needs a one-time setup. Manual tweaks outside the setup script drift.
Treat the server as replaceable.
Why BoringPush is the way to do this
From laptop to live in one command.
BoringPush sets up the server once. Then bp secret sync and bp deploy do the rest. The deploy waits for a health check.
Set up once. Ship forever.
FAQ
Does this work from a Windows laptop?
Yes, with WSL. Deploys travel over SSH and the server runs Linux whatever your laptop uses. Run the commands from the Linux side so script line endings stay correct.
What about preview environments per branch?
You can run a second copy of the app on the same server under a staging subdomain. Push to preview, check it, then ship to production. The kit does not do this for you automatically.
How do database changes travel with a deploy?
Production database changes are additive, and an agent cannot run them against production. Seed data never travels. Data moves deliberately, with a snapshot taken first.
Can the setup target hosts other than Hetzner?
Any Ubuntu 22.04 or 24.04 server: Hetzner, DigitalOcean, Linode or spare hardware. Host choice is price shopping, not architecture.
What breaks most often on first deploys?
Settings drift, the sign-in redirect address not matching the live domain, webhooks pointing at test addresses, and DNS patience. All four are documented and none is surprising twice.
When is one-command deploy the wrong goal?
When you run many services with their own lifecycles, such as separate API workers and queues. One-command shipping serves a single app well, and most solo SaaS products are exactly that. Move to orchestration when services multiply, not before.
Keep reading
- Deploy Next.js on Hetzner $5 by Dinner Tonight — the afternoon build this last mile lands on
- git push production deploy: Skip the 9-Minute CI Queue — the hook at the heart of the push
- Env Sync in 30 Seconds: Kill Secret Drift Forever — the secret half of the push
+ all the boring stuff (analytics, auth, SQLite ↔ Supabase, full marketplace, webhooks)
Command from your phone.
Built so autonomous AI agents build what you want without breaking what's already paying you.
- Built-in analytics — cookie-free mode, $0 forever, no PostHog bill
- SQLite ↔ Supabase: one setting, same field names
- Full digital marketplace & feedback boards included
- 3 payment rails: Stripe, LemonSqueezy & Dodo Payments
- Agent fences: lint gate, read-only data access, auto-rollback
- Ship from your phone via SSH & WireGuard/Tailscale in under a minute
$199
Get BoringPushOne-time payment · Lifetime updates · Unlimited projects.
Keep reading


