Self-hosted container deployment. Push an image, declare an app, deploy it.
- Website: https://bnema.dev/gordon
- Documentation: Docs | Wiki
- Discuss: GitHub Discussions
Gordon is a private image container registry, an app runtime, and a reverse proxy for your VPS.
The flow is explicit: you build an image locally, push it to your registry then declare the app in a standalone TOML file, apply it, deploy and it's live.
# Install the latest stable release to ~/.local/bin
curl -fsSL https://bnema.dev/gordon/install | sh
# Start the server (restart your shell first if the installer updated PATH)
gordon serveSee the installation guide for more.
Config is created at ~/.config/gordon/gordon.toml. See the Getting Started guide for full setup.
Example of the flow :
# Push the image (OCI transfer only, never deploys)
gordon images push myapp --build --remote prod
# Declare the app (blog.toml references the pushed tag)
gordon apps apply --file blog.toml --remote prod
# Activate it
gordon apps deploy blog --remote prod
# Check status
gordon daemon status
gordon apps status blog --remote prodMinimal app file (blog.toml):
name = "blog"
[services.web]
image = "gordon.mydomain.com/myapp:v1.2.0"
[[services.web.http]]
host = "blog.mydomain.com"
port = 3000Manage lifecycle and secrets — all from the command line:
gordon apps restart blog --remote prod # Restart from pinned digests
gordon apps stop blog --remote prod # Stop, preserve all data
gordon apps secrets set blog --service web DATABASE_URL=... --remote prodPush to Gordon's registry from any CI pipeline, then apply and deploy with the CLI. Push never triggers a deploy by itself.
- uses: bnema/gordon/.github/actions/deploy@main
with:
registry: registry.mydomain.com
username: ${{ secrets.GORDON_USERNAME }}
password: ${{ secrets.GORDON_TOKEN }}See the Deploy Action README for multi-platform builds, monorepo support, and all available options.
docker login gordon.mydomain.com
docker build -t gordon.mydomain.com/myapp:v1.0.0 .
docker push gordon.mydomain.com/myapp:v1.0.0
# Then, with the Gordon binary:
gordon apps apply --file blog.toml --remote prod
gordon apps deploy blog --remote prod| Command | Description |
|---|---|
gordon serve |
Start the Gordon server |
gordon daemon status |
Show server and app fleet status |
gordon daemon logs |
Show Gordon process logs |
gordon daemon reload |
Reload installation configuration |
gordon daemon config show |
Display installation configuration |
| Command | Description |
|---|---|
gordon apps apply --file FILE |
Validate and persist an app manifest |
gordon apps deploy APP |
Activate an app revision |
gordon apps list |
List applications |
gordon apps show APP |
Show desired and active state |
gordon apps status APP |
Show effective vs observed state |
gordon apps logs APP |
Show logs for an app service |
gordon apps restart APP |
Restart from pinned digests |
gordon apps stop APP |
Stop, preserve all data |
gordon apps start APP |
Start a stopped app |
gordon apps remove APP |
Remove workloads (volumes/secrets retained) |
| Command | Description |
|---|---|
gordon images push [image] |
Tag and push an image (never deploys) |
gordon images list |
List runtime and registry images |
gordon images prune |
Clean up dangling images and old tags |
gordon images tags <repo> |
List registry tags for a repository |
| Command | Description |
|---|---|
gordon apps secrets list APP |
List app secret names |
gordon apps secrets set APP --service SVC KEY=VAL |
Set app secret values |
| Command | Description |
|---|---|
gordon remotes list |
List remote Gordon endpoints |
gordon remotes add <name> <url> |
Add a remote |
gordon remotes use <name> |
Set the active remote |
gordon auth login |
Authenticate to a remote |
gordon auth token generate |
Generate a JWT token |
- Private Docker/Podman registry on your VPS
- Declarative apps: one TOML file per app, explicit apply then deploy
- Domain-to-container routing through a smart TCP edge reverse proxy
- Sequential service replacement (withdraw traffic, replace one generation, verify readiness, republish)
- Remote CLI management (daemon is the sole writer)
- Declarative per-service volumes, retained across lifecycle operations
- Per-service secrets in pass, app-wide public env in the manifest
- Per-app private networks plus opt-in shared networks
- Single binary
Note
Gordon exposes public traffic through entrypoints such as [entrypoints.edge] with protocol = "smart_tcp". It can terminate TLS via static certificates, public ACME certificates, or its internal CA. Cloudflare and upstream reverse proxies are optional deployment choices, not requirements. Use Gordon's ownership-aware volume commands; runtime commands such as docker volume prune bypass Gordon's retention checks.
Full documentation at bnema.dev/gordon
GPL-3.0 — Use freely, contribute back.