A native CLI that ships your containers to any Linux box - Podman Quadlets for supervision, kamal-proxy for zero-downtime cutovers, and no registry required.
curl -fsSL https://meridian-deploy.dev/install.sh | shservice: my-app
image: ghcr.io/acme/my-app
servers:
web:
hosts:
- prod-01.example.com
proxy:
host: my-app.example.com
ssl: true
app_port: 8000
healthcheck:
path: /up
workers:
hosts:
- prod-02.example.com
cmd: bin/jobs
transfer:
mode: stream
env:
clear:
RAILS_ENV: production
secret:
- DATABASE_URL
- SECRET_KEY_BASE
accessories:
db:
image: docker.io/library/postgres:16
host: prod-03.example.com
port: "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
env:
secret:
- POSTGRES_PASSWORD A real deploy.yml from a fleet with a web role, a worker role, and a Postgres accessory. Every key here is accepted by Meridian's strict YAML loader - nothing aspirational, nothing trimmed for the marketing page.
stream, incremental, or omit to pull from a registry.Secret= directives.No Kubernetes. No Docker Swarm. No CI/CD rebuild loop. Meridian is an imperative CLI that does what you tell it, when you tell it. A fresh host is prepared once; after that the loop is build and deploy. Full sequence in the Quickstart.
Meridian scans your project, picks up Marten, Rails, Elixir, Node or Go, and writes a working deploy.yml you can read and edit.
Bootstrap provisions a fresh Debian or Ubuntu box - deploy user, Podman, rootless directories, hardened SSH. Setup adds the service and proxy networks and starts kamal-proxy.
Send the image you've already built to each host - registry pull, SSH stream, or rsync'd OCI layout. systemd takes over through a Quadlet unit, and kamal-proxy switches traffic only after health checks pass.
Most deploy stacks ask you to adopt a worldview. Declarative reconciliation loops, control planes, custom resource definitions - a full philosophy for managing fleets you do not have.
Meridian is smaller on purpose. It does one job: take a container from your laptop and run it on a Linux server you rent, own, or colocate. When you push again, it cuts over cleanly. When something breaks, you read a systemd log.
No orchestrator. No registry tax. No daemon. A tool, not a platform.
Rootless-first is the right default in 2026. Quadlets let systemd supervise containers natively - the same system that already supervises your SSH daemon and cron jobs.
One compiled executable. No Ruby, no Python, no Node runtime on the server. Official Linux release builds can ship as a single file; local Crystal builds may still link shared libraries.
Your containers run under systemd. Restart policies, journald logs, and dependency ordering you already understand.
Zero-downtime cutovers with health checks. If the new container fails, traffic stays on the old one. No drama.
Keep pulling from a registry, or flip transfer.mode to stream or incremental and Meridian ships images over your existing SSH connection - no Docker Hub bill, no ECR setup, no CI upload step.
meridian init recognizes Marten, Rails, Elixir, Node, and Go and writes a working config you can actually read.
meridian secret gen and meridian secret set push values over SSH stdin into Podman's secret store on every host, kept under whatever secret driver that host's Podman is configured to use. Names listed under env.secret are injected at container start through the Quadlet's Secret= directive - never committed to your repo, never baked into the image.
Publish fingerprinted static assets from the same deploy, on a separate asset host, without adding object storage or hand-written reverse proxy config.
If you're happy with Kamal or Dokku, you should probably stay there - both are mature. Meridian makes different trade-offs. Here's what you're buying into:
| Capability | Meridian | Kamal 2 | Dokku |
|---|---|---|---|
| Runtime | Podman + systemd | Docker daemon | Docker daemon |
| Supervision | Quadlets (systemd) | Docker restart | Docker restart |
| Image transfer | Direct over SSH | Registry required | Local build on host |
| Zero-downtime | kamal-proxy | kamal-proxy | nginx / Traefik |
| Rootless by default | Yes | No | No |
| Install | Native executable | Ruby gem | Bash bootstrap + apt |
| Config | YAML | YAML | Imperative CLI |
Three reasons:
systemd as first-class units, including restart policies, dependency ordering, and journald logging.transfer.mode: stream pipes podman save | zstd over SSH to podman load on the host - simple, no extra infrastructure. transfer.mode: incremental syncs an OCI layout via rsync, so repeat deploys typically send only the bytes that changed. If you already have SSH to the server, you have everything Meridian needs. deploy.yml and start them explicitly with meridian accessory start. They run as their own Quadlet units, so systemd handles their lifecycle consistently with everything else, on their own schedule instead of inside the rolling deploy. An accessory on the service network gates the deploy: the new colour won't start until it passes its readiness probe.