# Free Kamal Alternative: Docker to a VPS, No Registry

> Ship a Next.js Docker image to your own server with one command: no registry account, a one-step rollback, and a health check before it goes live.

Source: https://boringpush.com/blog/free-kamal-alternative-docker-vps-no-registry

## You need a registry just to move one image

The usual path for a container deploy is: build the image, push it to a registry, then have the server pull it. For one app on one server, that's an extra account, an extra credential and an extra place for your image to live.

Kamal, the Rails team's deploy tool, describes itself on its site (checked 2026-10-20) as "Capistrano for Containers" that can deploy web apps "anywhere," onto "vanilla Ubuntu servers" with an SSH key, which it provisions with Docker. It's a well-known way to get container deploys onto your own servers.

This post is about a smaller route for a Next.js app, in the project you already have.

## Why we built our own

Kamal's idea is a good one: put the new version next to the old one, check it's alive, switch over, and keep the old one close. We built that idea into BoringPush for the smallest case, one app on one server.

There are two routes, and you choose. The default route is bare metal: your computer builds, the server swaps, and the previous version is always one command away. The Docker route does the same with containers, and it's the one this post is about.

## What you get

One command builds your app into a container and sends it straight to your server. No registry account, no registry password, and nothing to sign up for. Your SSH key is the only credential.

If the new version doesn't come up healthy, the last good one is brought back. And your data stays put through every release.

## The command

```bash
VPS_HOST=your-server bp docker ship
```

Here's what you'll see, in order:

1. **A check that both machines have Docker.** If the server doesn't, you get the one-line install command.
2. **A code check.** A problem stops the deploy before anything is built.
3. **A build** made for the kind of server you have, tagged with your latest commit.
4. **The image sent over SSH,** with nothing in between.
5. **The old version parked** as "previous", and the new one made live.
6. **A health check.** The new container has to answer before the deploy counts as done.
7. **An automatic undo** if it doesn't: the previous version comes back and you're told it failed.
8. **A tidy-up** that keeps only the live and previous versions, so your disk doesn't fill.

Undo a release by hand at any time:

```bash
VPS_HOST=your-server bp docker ship --rollback
```

On the very first deploy there's no previous version yet, and you're told so.

## Your data and secrets stay put

Your database lives in its own storage, outside the container, so a new version keeps the same data. Secrets come from a file on your server and are never baked into the image.

The app is reachable only through your web server, not directly from the internet. And the container restarts by itself if it ever stops. The README lists the image at about 285 MB.

## Kamal next to this script

| | Kamal | BoringPush Docker ship |
|---|---|---|
| Registry | Not covered in the pages I checked | None |
| Servers | Many, per Kamal's pitch | One |
| Server setup | Provisions Docker for you, per Kamal's site | You install Docker (you get the command) |
| Zero-downtime method | Described by Kamal; I didn't verify how | Starts the new version, checks health, otherwise restores the previous |
| Setup | Kamal's own config file | A few settings and one command |

I checked only Kamal's front page for this post, so read its docs for details I've marked as unverified. The honest summary: Kamal is built for fleets, and this is built for a single box.

## Who this fits

It fits one app on one server, where you already like containers. You get the safety of a health check and a previous version, without learning a new tool or opening a registry account.

If you run several servers, or want a managed rollout across them, look at a fleet tool like Kamal instead.

## Know this first

- **Not zero-downtime.** Replacing a container can leave a short gap while the new one starts. If that matters, use the default deploy route, which keeps the old version serving until the new one is ready.
- **One server only.** There's no rolling release across machines.
- **Scheduled jobs aren't set up for you.** They're set up on the default route. With Docker, add the same schedule to your server yourself.
- **A big upload each time.** The whole image goes over SSH, though compression helps.

## Why BoringPush is the way to do this

Containers shouldn't need a registry account to move one app to one server. In BoringPush the image goes straight to your server, the old version is kept, and a bad release undoes itself.

It's the shortest path from a Docker image to a live app, and it's already built into the kit you're using.

## FAQ

### Do I need Docker Hub or any registry?

No. The image goes from your machine to your server over SSH. You need Docker installed on both.

### Can I use it for a staging server?

Yes. Give the second app its own name and port, and you get a separate staging copy next to production.

### What happens to my database on deploy?

It stays where it is. The database lives in separate storage that the container mounts, so a new version picks up the same data. Back it up like any database file: see [Back Up SQLite With VACUUM INTO](/blog/backup-sqlite-vacuum-into-snapshots).

### How is this different from the default deploy?

The default route builds on your laptop and swaps the new version in. Docker builds a container and swaps container versions. Pick Docker if you want isolation or already run containers; the [Docker Compose and SQLite post](/blog/docker-compose-nextjs-sqlite-vps) covers the storage pitfalls.

### What if the new image is unhealthy?

The previous version is restored and the command tells you it failed. If there's no previous version (your first deploy), there's nothing to restore, and you'll need to fix it forward.

## Keep reading

- [Docker Compose + Next.js: Don't Let It Eat Your SQLite](/blog/docker-compose-nextjs-sqlite-vps) — the storage details
- [Free Vercel Alternative: Host Next.js on a $6 VPS](/blog/free-vercel-alternative-nextjs-vps) — the default deploy route
- [Free GitHub Actions Alternative: Deploy Without CI Minutes](/blog/free-github-actions-alternative-deploy-nextjs) — no hosted runners involved

[Get BoringPush →](/#get)
