Gordon
[License: GPL-3.0]
Self-hosted container deployment. Push an image, declare an app, deploy it.
What is Gordon?
Gordon is a private container registry, an app runtime, and a reverse proxy for your VPS.
The flow is explicit: build and push an image, declare the app in a standalone TOML file, apply it, then deploy. Push transfers OCI content only — it never deploys.
Quick Start
# 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 serve
The installer uses ~/.local/bin without sudo and can add the effective install directory to Fish, Bash, or Zsh PATH configuration. Set GORDON_UPDATE_PATH=1 to update PATH without prompting or GORDON_UPDATE_PATH=0 to leave configuration unchanged. Override the destination with an absolute path such as GORDON_INSTALL_DIR="$HOME/bin", GORDON_INSTALL_DIR="$HOME/.local/bin", or GORDON_INSTALL_DIR=/usr/local/bin.
To build the current next branch commit locally, use GORDON_CHANNEL=next. This is an unverified development source build, not a checksum-verified release, and requires a compatible Go toolchain. See the installation guide.
Config is created at ~/.config/gordon/gordon.toml. See the Getting Started guide for full setup.
Deploy with the CLI
Build locally, push to your Gordon server, declare the app, deploy:
# 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 prod
Minimal app file (blog.toml):
name = "blog"
[services.web]
image = "gordon.mydomain.com/myapp:v1.2.0"
[[services.web.http]]
host = "blog.mydomain.com"
port = 3000
Manage 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 prod
Deploy from CI/CD
Push to Gordon's registry from any CI pipeline, then apply and deploy with the CLI. Push never triggers a deploy by itself.
GitHub Actions
- 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 CLI + Gordon CLI
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
CLI Commands
Server
| 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 |
Applications
| 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) |
Images & Registry
| 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 |
Secrets
| Command |
Description |
gordon apps secrets list APP |
List app secret names |
gordon apps secrets set APP --service SVC KEY=VAL |
Set app secret values |
Remotes & Auth
| 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 |
Features
- 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.
Documentation
Full documentation at bnema.dev/gordon
- Docs — Installation, configuration, CLI reference
- Wiki — Tutorials, guides, and examples
License
GPL-3.0 — Use freely, contribute back.
source
1# Gordon
2
3[](https://www.gnu.org/licenses/gpl-3.0)
4
5Self-hosted container deployment. Push an image, declare an app, deploy it.
6
7- Website: https://bnema.dev/gordon
8- Documentation: [Docs](https://bnema.dev/gordon/docs) | [Wiki](https://bnema.dev/gordon/wiki)
9- Discuss: [GitHub Discussions](https://github.com/bnema/gordon/discussions)
10
11---
12
13## What is Gordon?
14
15Gordon is a private container registry, an app runtime, and a reverse proxy for your VPS.
16
17The flow is explicit: build and push an image, declare the app in a standalone TOML file, apply it, then deploy. Push transfers OCI content only — it never deploys.
18
19## Quick Start
20
21```bash
22# Install the latest stable release to ~/.local/bin
23curl -fsSL https://bnema.dev/gordon/install | sh
24
25# Start the server (restart your shell first if the installer updated PATH)
26gordon serve
27```
28
29The installer uses `~/.local/bin` without `sudo` and can add the effective install directory to Fish, Bash, or Zsh PATH configuration. Set `GORDON_UPDATE_PATH=1` to update PATH without prompting or `GORDON_UPDATE_PATH=0` to leave configuration unchanged. Override the destination with an absolute path such as `GORDON_INSTALL_DIR="$HOME/bin"`, `GORDON_INSTALL_DIR="$HOME/.local/bin"`, or `GORDON_INSTALL_DIR=/usr/local/bin`.
30
31To build the current `next` branch commit locally, use `GORDON_CHANNEL=next`. This is an unverified development source build, not a checksum-verified release, and requires a compatible Go toolchain. See the [installation guide](https://bnema.dev/gordon/docs/installation#choosing-an-install-channel).
32
33Config is created at `~/.config/gordon/gordon.toml`. See the [Getting Started guide](https://bnema.dev/gordon/docs/getting-started) for full setup.
34
35## Deploy with the CLI
36
37Build locally, push to your Gordon server, declare the app, deploy:
38
39```bash
40# Push the image (OCI transfer only, never deploys)
41gordon images push myapp --build --remote prod
42
43# Declare the app (blog.toml references the pushed tag)
44gordon apps apply --file blog.toml --remote prod
45
46# Activate it
47gordon apps deploy blog --remote prod
48
49# Check status
50gordon daemon status
51gordon apps status blog --remote prod
52```
53
54Minimal app file (`blog.toml`):
55
56```toml
57name = "blog"
58
59[services.web]
60image = "gordon.mydomain.com/myapp:v1.2.0"
61
62[[services.web.http]]
63host = "blog.mydomain.com"
64port = 3000
65```
66
67Manage lifecycle and secrets — all from the command line:
68
69```bash
70gordon apps restart blog --remote prod # Restart from pinned digests
71gordon apps stop blog --remote prod # Stop, preserve all data
72gordon apps secrets set blog --service web DATABASE_URL=... --remote prod
73```
74
75## Deploy from CI/CD
76
77Push to Gordon's registry from any CI pipeline, then apply and deploy with the CLI. Push never triggers a deploy by itself.
78
79### GitHub Actions
80
81```yaml
82- uses: bnema/gordon/.github/actions/deploy@main
83 with:
84 registry: registry.mydomain.com
85 username: ${{ secrets.GORDON_USERNAME }}
86 password: ${{ secrets.GORDON_TOKEN }}
87```
88
89See the [Deploy Action README](.github/actions/deploy/README.md) for multi-platform builds, monorepo support, and all available options.
90
91### Docker CLI + Gordon CLI
92
93```bash
94docker login gordon.mydomain.com
95docker build -t gordon.mydomain.com/myapp:v1.0.0 .
96docker push gordon.mydomain.com/myapp:v1.0.0
97# Then, with the Gordon binary:
98gordon apps apply --file blog.toml --remote prod
99gordon apps deploy blog --remote prod
100```
101
102## CLI Commands
103
104### Server
105
106| Command | Description |
107|---------|-------------|
108| `gordon serve` | Start the Gordon server |
109| `gordon daemon status` | Show server and app fleet status |
110| `gordon daemon logs` | Show Gordon process logs |
111| `gordon daemon reload` | Reload installation configuration |
112| `gordon daemon config show` | Display installation configuration |
113
114### Applications
115
116| Command | Description |
117|---------|-------------|
118| `gordon apps apply --file FILE` | Validate and persist an app manifest |
119| `gordon apps deploy APP` | Activate an app revision |
120| `gordon apps list` | List applications |
121| `gordon apps show APP` | Show desired and active state |
122| `gordon apps status APP` | Show effective vs observed state |
123| `gordon apps logs APP` | Show logs for an app service |
124| `gordon apps restart APP` | Restart from pinned digests |
125| `gordon apps stop APP` | Stop, preserve all data |
126| `gordon apps start APP` | Start a stopped app |
127| `gordon apps remove APP` | Remove workloads (volumes/secrets retained) |
128
129### Images & Registry
130
131| Command | Description |
132|---------|-------------|
133| `gordon images push [image]` | Tag and push an image (never deploys) |
134| `gordon images list` | List runtime and registry images |
135| `gordon images prune` | Clean up dangling images and old tags |
136| `gordon images tags <repo>` | List registry tags for a repository |
137
138### Secrets
139
140| Command | Description |
141|---------|-------------|
142| `gordon apps secrets list APP` | List app secret names |
143| `gordon apps secrets set APP --service SVC KEY=VAL` | Set app secret values |
144
145### Remotes & Auth
146
147| Command | Description |
148|---------|-------------|
149| `gordon remotes list` | List remote Gordon endpoints |
150| `gordon remotes add <name> <url>` | Add a remote |
151| `gordon remotes use <name>` | Set the active remote |
152| `gordon auth login` | Authenticate to a remote |
153| `gordon auth token generate` | Generate a JWT token |
154
155## Features
156
157- Private Docker/Podman registry on your VPS
158- Declarative apps: one TOML file per app, explicit apply then deploy
159- Domain-to-container routing through a smart TCP edge reverse proxy
160- Sequential service replacement (withdraw traffic, replace one generation, verify readiness, republish)
161- Remote CLI management (daemon is the sole writer)
162- Declarative per-service volumes, retained across lifecycle operations
163- Per-service secrets in pass, app-wide public env in the manifest
164- Per-app private networks plus opt-in shared networks
165- Single binary
166
167> [!NOTE]
168> 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.
169
170## Documentation
171
172Full documentation at **[bnema.dev/gordon](https://bnema.dev/gordon)**
173
174- [Docs](https://bnema.dev/gordon/docs) — Installation, configuration, CLI reference
175- [Wiki](https://bnema.dev/gordon/wiki) — Tutorials, guides, and examples
176
177## Community
178
179- [Report bugs](https://github.com/bnema/gordon/issues)
180- [Discussions](https://github.com/bnema/gordon/discussions)
181- [Submit PRs](https://github.com/bnema/gordon/pulls)
182
183## License
184
185GPL-3.0 — Use freely, contribute back.