Gordon
[License: AGPL-3.0]
Self-hosted container deployment. Push an image, declare an app, deploy it.
What is Gordon?
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.
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
See the installation guide for more.
Config is created at ~/.config/gordon/gordon.toml. See the Getting Started guide for full setup.
Deploy with the CLI
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 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
- Zero-downtime replacement of stateless TCP/HTTP services (start, verify readiness, switch traffic, stop the old one); services with volumes, binds, devices, or UDP interfaces are replaced sequentially
- 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
AGPL-3.0. If you run a modified Gordon as a network service, you must offer its source code to its users.
source
1# Gordon
2
3[](https://www.gnu.org/licenses/agpl-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 image container registry, an app runtime, and a reverse proxy for your VPS.
16
17The 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.
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```
28See the [installation guide](https://bnema.dev/gordon/docs/installation#choosing-an-install-channel) for more.
29
30Config is created at `~/.config/gordon/gordon.toml`. See the [Getting Started guide](https://bnema.dev/gordon/docs/getting-started) for full setup.
31
32## Deploy with the CLI
33
34Example of the flow :
35
36```bash
37# Push the image (OCI transfer only, never deploys)
38gordon images push myapp --build --remote prod
39
40# Declare the app (blog.toml references the pushed tag)
41gordon apps apply --file blog.toml --remote prod
42
43# Activate it
44gordon apps deploy blog --remote prod
45
46# Check status
47gordon daemon status
48gordon apps status blog --remote prod
49```
50
51Minimal app file (`blog.toml`):
52
53```toml
54name = "blog"
55
56[services.web]
57image = "gordon.mydomain.com/myapp:v1.2.0"
58
59[[services.web.http]]
60host = "blog.mydomain.com"
61port = 3000
62```
63
64Manage lifecycle and secrets — all from the command line:
65
66```bash
67gordon apps restart blog --remote prod # Restart from pinned digests
68gordon apps stop blog --remote prod # Stop, preserve all data
69gordon apps secrets set blog --service web DATABASE_URL=... --remote prod
70```
71
72## Deploy from CI/CD
73
74Push to Gordon's registry from any CI pipeline, then apply and deploy with the CLI. Push never triggers a deploy by itself.
75
76### GitHub Actions
77
78```yaml
79- uses: bnema/gordon/.github/actions/deploy@main
80 with:
81 registry: registry.mydomain.com
82 username: ${{ secrets.GORDON_USERNAME }}
83 password: ${{ secrets.GORDON_TOKEN }}
84```
85
86See the [Deploy Action README](.github/actions/deploy/README.md) for multi-platform builds, monorepo support, and all available options.
87
88### Docker CLI + Gordon CLI
89
90```bash
91docker login gordon.mydomain.com
92docker build -t gordon.mydomain.com/myapp:v1.0.0 .
93docker push gordon.mydomain.com/myapp:v1.0.0
94# Then, with the Gordon binary:
95gordon apps apply --file blog.toml --remote prod
96gordon apps deploy blog --remote prod
97```
98
99## CLI Commands
100
101### Server
102
103| Command | Description |
104|---------|-------------|
105| `gordon serve` | Start the Gordon server |
106| `gordon daemon status` | Show server and app fleet status |
107| `gordon daemon logs` | Show Gordon process logs |
108| `gordon daemon reload` | Reload installation configuration |
109| `gordon daemon config show` | Display installation configuration |
110
111### Applications
112
113| Command | Description |
114|---------|-------------|
115| `gordon apps apply --file FILE` | Validate and persist an app manifest |
116| `gordon apps deploy APP` | Activate an app revision |
117| `gordon apps list` | List applications |
118| `gordon apps show APP` | Show desired and active state |
119| `gordon apps status APP` | Show effective vs observed state |
120| `gordon apps logs APP` | Show logs for an app service |
121| `gordon apps restart APP` | Restart from pinned digests |
122| `gordon apps stop APP` | Stop, preserve all data |
123| `gordon apps start APP` | Start a stopped app |
124| `gordon apps remove APP` | Remove workloads (volumes/secrets retained) |
125
126### Images & Registry
127
128| Command | Description |
129|---------|-------------|
130| `gordon images push [image]` | Tag and push an image (never deploys) |
131| `gordon images list` | List runtime and registry images |
132| `gordon images prune` | Clean up dangling images and old tags |
133| `gordon images tags <repo>` | List registry tags for a repository |
134
135### Secrets
136
137| Command | Description |
138|---------|-------------|
139| `gordon apps secrets list APP` | List app secret names |
140| `gordon apps secrets set APP --service SVC KEY=VAL` | Set app secret values |
141
142### Remotes & Auth
143
144| Command | Description |
145|---------|-------------|
146| `gordon remotes list` | List remote Gordon endpoints |
147| `gordon remotes add <name> <url>` | Add a remote |
148| `gordon remotes use <name>` | Set the active remote |
149| `gordon auth login` | Authenticate to a remote |
150| `gordon auth token generate` | Generate a JWT token |
151
152## Features
153
154- Private Docker/Podman registry on your VPS
155- Declarative apps: one TOML file per app, explicit apply then deploy
156- Domain-to-container routing through a smart TCP edge reverse proxy
157- Zero-downtime replacement of stateless TCP/HTTP services (start, verify readiness, switch traffic, stop the old one); services with volumes, binds, devices, or UDP interfaces are replaced sequentially
158- Remote CLI management (daemon is the sole writer)
159- Declarative per-service volumes, retained across lifecycle operations
160- Per-service secrets in pass, app-wide public env in the manifest
161- Per-app private networks plus opt-in shared networks
162- Single binary
163
164> [!NOTE]
165> 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.
166
167## Documentation
168
169Full documentation at **[bnema.dev/gordon](https://bnema.dev/gordon)**
170
171- [Docs](https://bnema.dev/gordon/docs) — Installation, configuration, CLI reference
172- [Wiki](https://bnema.dev/gordon/wiki) — Tutorials, guides, and examples
173
174## Community
175
176- [Report bugs](https://github.com/bnema/gordon/issues)
177- [Discussions](https://github.com/bnema/gordon/discussions)
178- [Submit PRs](https://github.com/bnema/gordon/pulls)
179
180## License
181
182[AGPL-3.0](LICENSE). If you run a modified Gordon as a network service, you must offer its source code to its users.