Shipping Web Apps to a VPS Should Be This Simple
Source: Dev.to
Why deploying to a VPS still feels complicated
I like servers—not in a “let me spend Saturday hand‑tuning nginx” way, but in a “this $6 VPS could probably run half my side projects” way.
For many small and medium web apps, the app itself isn’t the hard part; the surrounding chores are:
- building the app
- getting it onto the server
- starting it without dropping requests
- setting up HTTPS
- keeping secrets out of random
.envfiles - seeing logs without SSH archaeology
- making local dev resemble production so cookies and OAuth stop being weird
None of this is impossible; it’s just a stack of little tasks. That stack is why I started working on Tako.
Introducing Tako
Tako aims to make deployment feel as simple as:
tako init
tako deploy
Not because two commands are magical, but because most apps don’t need a custom infrastructure thesis before they can serve HTTP.
Tako is a CLI plus a tiny server runtime. You install the server once on your VPS, add that server locally, then deploy from your project directory.
Deploy flow
The flow is intentionally plain:
- Build locally
- Package the output
- Upload it over SSH/SFTP
- Unpack it on the server
- Start the new version
- Roll traffic over without dropping the old version first
No registry. No image push. No Kubernetes object graph. No “where did this container layer come from?” moment at 1 AM.
Runtime and proxy model
Tako runs your app as a normal process. For JavaScript and TypeScript apps there’s a small tako.sh SDK. Your app tells Tako when it’s ready by reporting the port it bound to, after which the server proxy can safely route traffic to it.
In production:
- App instances bind to
127.0.0.1on an assigned port. - Tako’s proxy sits in front, handles the public route, and forwards requests only to healthy instances.
Benefits
- Rolling deploys can start a new instance before removing the old one.
- Low‑traffic apps can opt into scale‑to‑zero.
- Logs and status are attached to the process Tako actually started.
- The app doesn’t need to know the public port or hostname.
Configuration
The config is meant to stay boring too:
name = "my-app"
runtime = "bun"
preset = "tanstack-start"
[build]
run = "bun run build"
[envs.production]
route = "my-app.example.com"
servers = ["main"]
More options are available when you need them (multiple environments, servers, build stages, release commands, secrets, scale settings), but the basic shape is just “here is my app, here is where it should run.”
Local development
Most deploy tools stop at production, leaving you with a mess of ports:
http://localhost:3000http://localhost:5173http://localhost:8787
When you need secure cookies, OAuth callbacks, service workers, a second app, or a webhook tunnel, your local setup suddenly gets its own personality.
tako dev tries to make local apps feel like real apps:
tako dev
It gives your app a .test domain with HTTPS, DNS, and a local proxy. Instead of remembering a port, you open something like:
https://my-app.test/
The goal isn’t to be fancy; it’s to remove the tiny paper cuts that make local dev drift away from production.
When Docker makes more sense
- If your app needs heavy OS‑level isolation, containers might be the better answer.
- If your team already has a container pipeline that works, you probably shouldn’t throw it away for fun.
- If you’re running a bunch of unrelated stacks with weird system packages, Docker is a reasonable boundary.
Tako is more opinionated: it focuses on common web‑app runtimes like Bun, Node, and Go, then tries to make that path fast and calm. That trade‑off is the whole point.
Getting started
The project is open source. Docs are here:
Quick install
curl -fsSL https://tako.sh/install.sh | sh
tako init
tako dev
Deploy to a server
sudo sh -c "$(curl -fsSL https://tako.sh/install-server.sh)"
tako servers add
tako deploy