Deploying Your First App with Neo
From a bare Dockerfile to a live HTTPS domain in one command — what neo init and neo deploy actually do.
What Neo needs from your project
Neo deploys Docker images over SSH — no agent, no control plane running on the server, no dashboard you need to keep patched. If your project has a Dockerfile that EXPOSEs a port, that's enough to start.
curl -fsSL https://neo.vxero.dev/neo | sh
neo activate # free, one time
Point Neo at a server
neo init [email protected]
This connects over SSH, checks the OS is supported (Ubuntu 24.04+, Debian, Fedora 39+, or RHEL-family 9+), installs Docker and Caddy if they're missing, and writes /etc/neo/state.json to track what's deployed there. It only touches Docker and Caddy — nothing else on the box.
Deploy
cd my-app
neo deploy --domain app.example.com
Neo builds the image (locally if Docker is available on your machine, otherwise on the server), transfers it, starts the new container, waits for it to pass a health check, then switches Caddy's route to it — the old container never stops serving until the new one is confirmed healthy. Caddy provisions a Let's Encrypt certificate automatically the first time a real domain resolves to the server.
Commit a .neo.yml
The command above works with zero configuration, but the domain, port and environment then live only in server state — not in your repository. For anything past a first deploy:
neo config init
This scaffolds a .neo.yml you commit alongside your Dockerfile, so the deployment is described in your repo, reviewable in a pull request, and reproducible from a fresh clone. See Understanding .neo.yml for why this file matters more than it looks like it should.
name: my-app
domain: app.example.com
port: 8080
From here, neo deploy with no arguments redeploys using this file. Everything else — background workers, persistent volumes, encrypted secrets, multiple environments — builds on top of this one file.
Try it yourself — Neo is free, with every feature unlocked.
Get started free