detached fb0f9df

docs(README): Simplify content

commit
fb0f9df34f22c0cff75755cff7c611bc5d34b19f
author
bamen · · 2025-06-09 18:18 UTC
parent
c4989a7
signature
unsigned
2 files · +248 -330
M README.md +183 -329
@@ -1,22 +1,34 @@
11 # Gordon
22
3+> Deploy containers to your VPS in seconds, not hours. One push, zero complexity.
4+
35 <div align="center">
46 <img src="assets/gordon-mascot-hq-trsp.png" alt="Gordon Mascot" width="200">
57 <h3>The Smart Way to Deploy Containers on Your VPS</h3>
6- <p><em>Push image → Auto-deploy → Zero complexity</em></p>
8+ <p><em>Push image → Auto-deploy → Zero downtime</em></p>
79 </div>
810
9-## Why Gordon?
11+## What is Gordon?
12+
13+Gordon is a lightweight deployment system that turns any VPS into a container hosting platform with push-to-deploy capabilities. It combines a private container registry with an intelligent reverse proxy to create a self-hosted alternative to complex orchestration systems.
1014
11-**You have a $5 VPS. You want to run multiple apps. You don't want complex CI/CD pipelines, expensive PaaS solutions, or overcomplicated orchestration.**
15+### Key Features
1216
13-Gordon is the missing piece that makes container deployment on budget VPS servers as simple as expensive PaaS solutions. One binary, one config file, unlimited apps.
17+- **Built-in Container Registry**: Your VPS becomes a private registry
18+- **Push-to-Deploy**: Pushing an image triggers automatic deployment
19+- **Smart Routing**: Multi-domain support with automatic HTTPS
20+- **Zero-Downtime Updates**: Graceful container swaps on new pushes
21+- **Network Isolation**: Each app gets its own network with attached services
22+- **Auto-Volume Management**: Persistent storage from Dockerfile VOLUME directives
23+- **Environment Merging**: Dockerfile ENV + your custom variables
24+- **Minimal Footprint**: ~15MB RAM usage, single binary
1425
1526 ### Perfect For
16-- **Solo developers** with multiple side projects on one VPS
17-- **Small teams** who want Heroku-like simplicity without the cost
18-- **Agencies** deploying client apps across VPS servers
19-- **Anyone** tired of complex deployment pipelines
27+
28+- **Solo developers** running multiple projects on one VPS
29+- **Small teams** wanting simple, reliable deployments
30+- **Agencies** managing client applications across servers
31+- **Anyone** tired of overengineered deployment solutions
2032
2133 ## How It Works
2234
@@ -29,96 +41,171 @@ podman run -p 8080:8080 myapp # Works? Great!
2941 podman tag myapp registry.mydomain.com/myapp:latest
3042 podman push registry.mydomain.com/myapp:latest
3143
32-# 3. That's it. Gordon deploys it automatically.
44+# 3. That's it. Gordon handles the rest.
3345 ```
3446
35-**No build servers. No CI/CD complexity. If it runs on your machine, it runs in production.**
47+**Your machine is the build server. If it runs locally, it runs in production.**
48+
49+## Quick Start (5 minutes)
3650
37-## Key Features
51+### Prerequisites
52+- Ubuntu/Debian VPS with root access
53+- Domain pointing to your VPS
54+- Cloudflare account (free tier works)
3855
39-- **Local-First Development**: Your machine is the build server
40-- **Built-in Container Registry**: Your VPS becomes a private registry
41-- **Push-to-Deploy**: New images deploy instantly
42-- **Multi-Domain Routing**: Unlimited apps with automatic HTTPS via Cloudflare
43-- **Zero-Downtime Updates**: Graceful container swaps
44-- **Network Isolation**: Each app gets its own isolated network with attached services (databases, caches, etc.)
45-- **Auto-Route Creation** (Optional): Push `myapp.mydomain.com:latest` → route created automatically
46-- **Auto-Volume Management**: Zero-config persistent storage from Dockerfile VOLUME directives
47-- **Auto-Environment Injection**: Dockerfile ENV directives automatically merge with your custom environment variables
56+### Installation
4857
49-## Quick Start (5 minutes)
58+```bash
59+# Download Gordon from the releases page
60+# https://github.com/bnema/gordon/releases/latest
61+# Example: wget https://github.com/bnema/gordon/releases/download/v2.0.0/gordon_2.0.0_linux_amd64.tar.gz
62+# Then extract:
63+tar -xzf gordon_*.tar.gz
64+chmod +x gordon
65+sudo mv gordon /usr/local/bin/
66+
67+# Create initial config
68+gordon start
69+# Press Ctrl+C after config is created
70+```
71+
72+**Important:** This is just the binary installation. For a complete working setup including networking, firewall, and systemd service, follow the [detailed setup guide](#detailed-setup-guide) below.
73+
74+## Core Concepts
75+
76+### Local-First Development
77+Your dev machine likely has 8-16 cores and 16-32GB RAM. Your VPS has 1-2 cores and 1-4GB RAM. Why build containers on the weak machine? Gordon lets you build locally and deploy the finished product.
78+
79+### Push-to-Deploy Workflow
80+```bash
81+# Initial deployment
82+podman build -t myapp .
83+podman push registry.mydomain.com/myapp:latest
84+# Visit https://app.mydomain.com
85+
86+# Update deployment
87+podman build -t myapp .
88+podman push registry.mydomain.com/myapp:latest
89+# Zero-downtime update applied automatically
90+```
91+
92+### Automatic Volume Management
93+```dockerfile
94+# In your Dockerfile
95+FROM postgres:15
96+VOLUME ["/var/lib/postgresql/data"] # Gordon creates persistent storage
97+
98+FROM node:18
99+VOLUME ["/app/uploads", "/app/data"] # Multiple volumes supported
100+```
101+
102+### Environment Configuration
103+Gordon automatically creates and loads env files based on domain names:
104+```bash
105+# Auto-created on first run: ~/.local/share/gordon/env/app_mydomain_com.env
106+# Just add your variables:
107+DATABASE_URL=postgresql://localhost:5432/myapp
108+API_KEY=sk-1234567890
109+NODE_ENV=production
110+```
111+Supports secrets from `pass` and `SOPS`. See [examples/](examples/) for advanced usage.
112+
113+### Network Isolation
114+Each app runs in its own isolated network by default. Attach services for internal communication:
115+```toml
116+[attachments]
117+"app.mydomain.com" = ["my-postgres:latest", "my-redis:latest"]
118+# Access services via hostname: my-postgres:5432, my-redis:6379
119+```
120+Gordon also supports network groups, see [examples/](examples/) for advanced usage.
121+
122+### Persistent Storage
123+- **Volumes**: Automatically created from Dockerfile VOLUME directives, persist across updates
124+
125+### Production ready logging
126+- **Logs**: All container output saved to `~/.local/share/gordon/logs/containers/`
127+- **Rotation**: Logs rotate at 100MB, keeping 3 backups for 28 days
50128
51-### 1. Setup VPS
129+## Configuration
130+
131+Gordon uses a single TOML file for all configuration:
132+
133+```toml
134+# ~/.config/gordon/gordon.toml
135+[server]
136+registry_domain = "registry.mydomain.com" # Required
137+
138+[registry_auth]
139+username = "admin" # Choose your credentials
140+password = "your-secure-password"
141+
142+[routes]
143+"app.mydomain.com" = "myapp:latest" # Domain → Image mapping
144+"api.mydomain.com" = "myapi:v2.1.0" # Pin specific versions
145+
146+# Optional: Attach services to apps
147+[attachments]
148+"app.mydomain.com" = ["postgres:latest", "redis:latest"]
149+# Services accessible via internal DNS: postgres:5432, redis:6379
150+```
151+
152+See [examples/](examples/) for advanced configurations including network groups and more.
153+
154+## Detailed Setup Guide (Using podman in rootless mode)
155+Note: Refer to Quick Start for the binary installation.
156+
157+### 1. VPS Preparation
52158 ```bash
53-# Install essentials on Ubuntu/Debian
54-sudo apt update && sudo apt install -y podman ufw
159+# Update system
160+sudo apt update && sudo apt upgrade -y
161+
162+# Install podman and firewall
163+sudo apt install -y podman ufw
55164
56165 # Configure firewall
57-sudo ufw --force enable
58-sudo ufw default deny incoming
59-sudo ufw default allow outgoing
60166 sudo ufw allow 22/tcp 80/tcp 443/tcp
61-sudo ufw enable
167+sudo default deny
168+sudo ufw --force enable
62169 ```
63170
64-### 2. Redirect Ports
171+### 2. Enable Port Forwarding
65172 ```bash
66-# Redirect 80/443 to 8080
173+# Forward 80/443 to unprivileged port 8080
67174 sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
68175 sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8080
176+
177+# Make persistent
69178 sudo apt install -y iptables-persistent
70179 sudo netfilter-persistent save
71180 ```
72181
73-### 3. Enable Rootless Containers
182+### 3. Configure Rootless Containers
74183 ```bash
184+# Enable user namespaces
75185 echo 'user.max_user_namespaces=28633' | sudo tee -a /etc/sysctl.conf
76186 sudo sysctl -p
187+
188+# Setup subuid/subgid
77189 sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
190+
191+# Enable podman socket
78192 systemctl --user enable --now podman.socket
79193 ```
80194
81-### 4. Configure Registry
195+### 4. Registry Configuration
82196 ```bash
83197 mkdir -p ~/.config/containers
84-tee ~/.config/containers/registries.conf > /dev/null <<EOF
85-[registries.search]
86-registries = ['docker.io', 'registry.mydomain.com']
87-
198+cat > ~/.config/containers/registries.conf <<EOF
88199 [registries.insecure]
89-registries = ['registry.mydomain.com']
200+registries = ['localhost:5000']
90201 EOF
91202 ```
92203
93-### 5. Install Gordon
94-```bash
95-wget https://github.com/bnema/gordon/releases/latest/download/gordon-linux-amd64
96-chmod +x gordon-linux-amd64
97-sudo mv gordon-linux-amd64 /usr/local/bin/gordon
98-```
99-
100-### 6. Configure Gordon
101-```bash
102-# Gordon creates a default config file on first run
103-# Edit it to set your registry domain and credentials:
104-# gordon.toml location: ./ → ~/.config/gordon/ → ~/.gordon/ → ~/ → /etc/gordon/
105-
106-# Key settings to update:
107-# - registry_domain = "registry.mydomain.com"
108-# - registry_auth username/password
109-# - routes for your domains
110-```
111-
112-### 7. Point DNS
113-```
114-A *.mydomain.com → YOUR_VPS_IP
115-A mydomain.com → YOUR_VPS_IP
116-```
117-
118-### 8. Create Service
204+### 5. Create Systemd Service
119205 ```bash
206+# Create user service
120207 mkdir -p ~/.config/systemd/user
121-tee ~/.config/systemd/user/gordon.service > /dev/null <<EOF
208+cat > ~/.config/systemd/user/gordon.service <<EOF
122209 [Unit]
123210 Description=Gordon Container Platform
124211 After=podman.socket
@@ -127,7 +214,6 @@ After=podman.socket
127214 Type=simple
128215 Restart=always
129216 RestartSec=5
130-Environment=CONTAINER_HOST=unix://%t/podman/podman.sock
131217 ExecStart=/usr/local/bin/gordon start
132218 WorkingDirectory=%h
133219
@@ -135,304 +221,72 @@ WorkingDirectory=%h
135221 WantedBy=default.target
136222 EOF
137223
224+# Enable and start service
138225 systemctl --user daemon-reload
139226 systemctl --user enable --now gordon
140227 sudo loginctl enable-linger $USER
141-```
142-
143-### 9. Deploy Your First App
144-```bash
145-# From your dev machine:
146-podman login registry.mydomain.com
147-# (Use the credentials from your gordon.toml)
148228
149-podman tag myapp:latest registry.mydomain.com/myapp:latest
150-podman push registry.mydomain.com/myapp:latest
151-# Visit https://app.mydomain.com
229+# Verify it's running
230+systemctl --user status gordon
152231 ```
153232
154-## Real-World Examples
233+## Deployment Strategies
155234
156-### Push-to-Deploy Workflow
235+### Simple Deployment
157236 ```bash
158237 # Build locally
159238 podman build -t myapp .
160239
161-# Push latest = instant deploy
162-podman push registry.mydomain.com/myapp:latest
163-```
164-
165-### Version Control & Rollbacks
240+# Test locally
241+podman run -p 8080:8080 myapp
166242
167-Gordon supports multiple rollback strategies. Choose the approach that fits your workflow:
243+# Push to registry
244+podman tag myapp registry.mydomain.com/myapp:latest
245+podman push registry.mydomain.com/myapp:latest
168246
169-#### Method 1: Tag-Based Rollbacks (Simple)
170-```bash
171-# Tag and push a specific version
172-podman tag myapp:v1.0.1 registry.mydomain.com/myapp:v1.0.1
173-podman push registry.mydomain.com/myapp:v1.0.1
174-
175-# Tag and push latest (v1.0.2 in this example)
176-podman tag myapp:v1.0.2 registry.mydomain.com/myapp:v1.0.2 && podman tag myapp:v1.0.2 registry.mydomain.com/myapp:latest
177-podman push registry.mydomain.com/myapp:v1.0.2 && podman push registry.mydomain.com/myapp:latest
178-
179-# Rollback: Update gordon.toml and reload
180-# From: "app.mydomain.com" = "myapp:latest"
181-# To: "app.mydomain.com" = "myapp:v1.0.1"
182-# Then: systemctl --user reload gordon
247+# Profit!
183248 ```
184249
185-#### Method 2: Manifest Annotation Deployment (Advanced)
250+### Versioned Deployment
186251 ```bash
187-# Build and push your versioned images first
188-export VERSION=v1.0.1
189-podman build --tag myapp:$VERSION --tag registry.mydomain.com/myapp:$VERSION .
190-podman push registry.mydomain.com/myapp:$VERSION
252+# Deploy specific version
253+podman tag myapp:v1.2.0 registry.mydomain.com/myapp:v1.2.0
254+podman push registry.mydomain.com/myapp:v1.2.0
191255
192-export VERSION=v1.0.2
193-podman build --tag myapp:$VERSION --tag registry.mydomain.com/myapp:$VERSION .
194-podman push registry.mydomain.com/myapp:$VERSION
256+# Update route in gordon.toml
257+# "app.mydomain.com" = "myapp:v1.2.0"
258+```
195259
196-# Deploy v1.0.2 with version annotation
197-export VERSION=v1.0.2
260+### Advanced: Manifest Annotations
261+```bash
262+# Deploy with metadata
263+export VERSION=v1.2.0
198264 podman manifest create myapp:latest
199265 podman manifest add myapp:latest registry.mydomain.com/myapp:$VERSION
200266 podman manifest annotate myapp:latest --annotation version=$VERSION registry.mydomain.com/myapp:$VERSION
201267 podman manifest push myapp:latest registry.mydomain.com/myapp:latest
202-
203-# Rollback to v1.0.1 (just change the version)
204-export VERSION=v1.0.1
205-podman manifest create myapp:latest --amend
206-podman manifest add myapp:latest registry.mydomain.com/myapp:$VERSION
207-podman manifest annotate myapp:latest --annotation version=$VERSION registry.mydomain.com/myapp:$VERSION
208-podman manifest push myapp:latest registry.mydomain.com/myapp:latest
209-# Gordon automatically deploys the specified version
210-```
211-
212-**Recommendation**: Use Method 2 for instant deployments without config file edits. Method 1 is best for simple workflows.
213-
214-### Auto-Route Creation
215-```toml
216-[auto_route]
217-enabled = true
218-```
219-
220-```bash
221-# Image name becomes route automatically
222-podman push registry.mydomain.com/staging.example.com:latest
223-# Creates: "staging.example.com" = "staging.example.com:latest"
224268 ```
225269
226-## Persistent Data with Volumes
227-
228-Gordon automatically detects `VOLUME` directives in your Dockerfiles and creates persistent named volumes for your containers. **Zero configuration required** - just add `VOLUME` instructions to your Dockerfile.
229-
230-### How It Works
270+## Community
231271
232-```dockerfile
233-# In your Dockerfile
234-FROM node:18-alpine
235-WORKDIR /app
236-COPY package*.json ./
237-RUN npm install
238-COPY . .
239-
240-# Gordon automatically creates volumes for these paths
241-VOLUME ["/app/data", "/app/uploads"]
242-
243-EXPOSE 3000
244-CMD ["npm", "start"]
245-```
246-
247-When you deploy this image, Gordon will:
248-1. **Detect** the `VOLUME ["/app/data", "/app/uploads"]` directive
249-2. **Create** named volumes like `gordon-app-example-com-a1b2c3d4` (one per path)
250-3. **Mount** them to your container automatically
251-4. **Preserve** data across container restarts and updates
252-
253-### Volume Features
254-
255-- **Automatic Detection**: No config needed - Gordon reads VOLUME directives from your images
256-- **Persistent Storage**: Data survives container restarts, updates, and VPS reboots
257-- **Zero Downtime**: Volumes re-attach instantly when containers restart
258-- **Predictable Naming**: `gordon-{domain}-{path-hash}` format for easy identification
259-- **Cross-Platform**: Works with Docker and Podman
260-
261-### Example Workflows
262-
263-#### Database Container
264-```dockerfile
265-FROM postgres:15
266-# Gordon auto-creates persistent volume for PostgreSQL data
267-VOLUME ["/var/lib/postgresql/data"]
268-```
269-
270-#### Web App with File Uploads
271-```dockerfile
272-FROM nginx:alpine
273-COPY dist/ /usr/share/nginx/html/
274-# Gordon auto-creates persistent volume for user uploads
275-VOLUME ["/usr/share/nginx/html/uploads"]
276-```
277-
278-#### Multi-Volume Application
279-```dockerfile
280-FROM myapp:latest
281-# Gordon creates separate volumes for each path
282-VOLUME ["/app/data", "/app/logs", "/app/cache"]
283-```
284-
285-### Volume Configuration (Optional)
286-
287-```toml
288-[volumes]
289-auto_create = true # Default: true (set to false to disable)
290-prefix = "gordon" # Default: "gordon" (volume name prefix)
291-preserve = true # Default: true (keep volumes when removing containers)
292-```
293-
294-**Most users never need to touch this configuration** - the defaults work perfectly for 99% of use cases.
295-
296-## Smart Environment & Volume Management
297-
298-Gordon automatically handles both environment variables and persistent storage with zero configuration:
299-
300-### Environment Variables
301-- **Auto-Detection**: Reads `ENV` directives from your Dockerfile
302-- **Smart Merging**: Your `.env` files override Dockerfile ENV values
303-- **Secret Integration**: Supports `pass` and `SOPS` for sensitive data
304-
305-### Persistent Volumes
306-- **Auto-Detection**: Reads `VOLUME` directives from your Dockerfile
307-- **Zero Configuration**: Creates and manages volumes automatically
308-- **Data Persistence**: Survives container updates and reboots
309-
310-See [examples/](examples/) for detailed configuration examples.
311-
312-## Advanced Configuration
313-
314-### Full Config Structure (with default values)
315-```toml
316-# gordon.toml - Auto-generated on first run
317-[server]
318-port = 8080 # Default: 8080
319-registry_port = 5000 # Default: 5000
320-registry_domain = "registry.mydomain.com" # No default (required)
321-runtime = "auto" # Default: "auto" (auto, docker, podman, podman-rootless)
322-socket_path = "" # Default: "" (auto-detected)
323-data_dir = "~/.local/share/gordon" # Default: ~/.local/share/gordon (or ./data for root)
324-ssl_email = "" # No default (optional)
325-# runtime auto-detects: docker → podman-rootless → podman
326-# Override with env: CONTAINER_HOST=unix:///custom/socket
327-
328-[registry_auth]
329-enabled = true # Default: true
330-username = "admin" # No default (required when enabled)
331-password = "your-secure-password" # No default (required when enabled)
332-
333-[routes]
334-"app.mydomain.com" = "myapp:latest" # Default: {} (empty map)
335-# Routes default to HTTPS unless prefixed with http://
336-
337-# Network isolation - each app gets its own isolated network
338-[attachments]
339-"app.mydomain.com" = ["my-postgres:latest", "my-redis:latest"]
340-# Services accessible via: postgres:5432, redis:6379 (internal DNS)
341-# Create custom Dockerfiles with VOLUME directives for persistence
342-
343-[auto_route]
344-enabled = false # Default: false
345-
346-[volumes]
347-auto_create = true # Default: true (automatically create volumes from VOLUME directives)
348-prefix = "gordon" # Default: "gordon" (volume name prefix)
349-preserve = true # Default: true (keep volumes when containers are removed)
350-# Volumes are auto-created from Dockerfile VOLUME directives
351-# Naming: gordon-{domain}-{path-hash} (e.g., gordon-app-example-com-a1b2c3d4)
352-
353-[env]
354-dir = "{data_dir}/env" # Default: {data_dir}/env
355-providers = ["pass", "sops"] # Default: ["pass", "sops"]
356-# Example env file: ./data/env/app_mydomain_com.env
357-# NODE_ENV=production
358-# DATABASE_URL=postgresql://localhost:5432/prod
359-# API_KEY=${pass:company/api-key} # Unix pass integration
360-# JWT_SECRET=${sops:secrets.yaml:jwt} # SOPS integration
361-
362-[logging]
363-enabled = true # Default: true
364-level = "info" # Default: "info" (debug, info, warn, error, fatal, panic)
365-dir = "{data_dir}/logs" # Default: {data_dir}/logs
366-main_log_file = "gordon.log" # Default: "gordon.log"
367-proxy_log_file = "proxy.log" # Default: "proxy.log"
368-container_log_dir = "containers" # Default: "containers"
369-max_size = 100 # Default: 100 (MB before rotation)
370-max_backups = 3 # Default: 3 (old files to keep)
371-max_age = 28 # Default: 28 (days to keep old files)
372-compress = true # Default: true (gzip rotated logs)
373-# Creates: gordon.log, proxy.log, containers/*.log
374-```
375-
376-## FAQ
377-
378-**Q: How is this different from Traefik/Nginx Proxy Manager?**
379-A: Those are just reverse proxies. Gordon is a complete deployment platform with built-in registry, automatic deployment, and container lifecycle management.
380-
381-**Q: Where do builds happen?**
382-A: On YOUR machine. If it runs locally, it runs in production.
383-
384-**Q: How do I rollback?**
385-A: Change the version annotation in your manifest and push, or edit gordon.toml. Takes seconds.
386-
387-**Q: Do I need Cloudflare?**
388-A: Yes, for SSL certificates and DDoS protection.
389-
390-**Q: Resource requirements?**
391-A: Runs on 1GB RAM VPS. Gordon uses <15MB RAM.
392-
393-**Q: How do I check if volumes are working?**
394-A: Use `docker volume ls` or `podman volume ls` to see Gordon-managed volumes (named `gordon-*`).
395-
396-**Q: Can I disable automatic volumes?**
397-A: Yes, set `volumes.auto_create = false` in your gordon.toml config.
398-
399-**Q: What happens to my data when I update containers?**
400-A: Volumes are preserved by default. Your data persists across updates and reboots.
401-
402-**Q: How do I backup volume data?**
403-A: Volumes are regular Docker/Podman volumes - use standard backup tools like `docker run --rm -v volume_name:/data -v $(pwd):/backup alpine tar czf /backup/backup.tar.gz -C /data .`
404-
405-**Q: Do Dockerfile ENV directives work automatically?**
406-A: Yes! Gordon reads ENV directives from your images and merges them with your custom .env files.
407-
408-**Q: What happens if I set the same environment variable in both Dockerfile and .env?**
409-A: Your .env file always wins - custom environment variables override Dockerfile ENV directives.
410-
411-**Q: Can I see what environment variables are being applied?**
412-A: Yes, check Gordon's logs - it shows both Dockerfile ENV and final merged environment variables.
413-
414-**Q: Do I need to rebuild my images to change environment variables?**
415-A: No! Just update your .env files and reload Gordon. Dockerfile ENV provides defaults; .env provides overrides.
272+Gordon is open source and welcomes contributions:
273+- [Report bugs](https://github.com/bnema/gordon/issues)
274+- [Suggest features](https://github.com/bnema/gordon/discussions)
275+- [Submit PRs](https://github.com/bnema/gordon/pulls)
416276
417277 ## Philosophy
418278
419-```bash
420-# Traditional CI/CD
421-push code → wait for build → hope it works → debug remotely → repeat
279+Traditional deployment pipelines recreate your development environment in CI/CD systems, adding complexity and points of failure. Gordon takes a different approach: **your development machine is already a perfect build environment**.
422280
423-# The Gordon Way
424-build locally → test locally → push image → instant deploy
281+```bash
282+# Traditional: Code → CI/CD → Build → Test → Deploy → Hope
283+# Gordon: Code → Build → Test → Push → Done
425284 ```
426285
427-Your machine already works. Why replicate it in CI/CD?
428-
429286 ## License
430287
431288 GPL-3.0 - Use freely, contribute back.
432289
433290 ---
434291
435-<div align="center">
436- <p><strong>Stop overcomplicating container deployment.</strong></p>
437- <p>Star this repo if Gordon saves you money!</p>
438-</div>⏎̸
292+**Built for developers who ship.** If Gordon helps you deploy faster, [give it a star](https://github.com/bnema/gordon).⏎̸
@@ -1,22 +1,34 @@
1 # Gordon1 # Gordon
2 2
3+> Deploy containers to your VPS in seconds, not hours. One push, zero complexity.
4+
3 <div align="center">5 <div align="center">
4 <img src="assets/gordon-mascot-hq-trsp.png" alt="Gordon Mascot" width="200">6 <img src="assets/gordon-mascot-hq-trsp.png" alt="Gordon Mascot" width="200">
5 <h3>The Smart Way to Deploy Containers on Your VPS</h3>7 <h3>The Smart Way to Deploy Containers on Your VPS</h3>
6- <p><em>Push image → Auto-deploy → Zero complexity</em></p>8+ <p><em>Push image → Auto-deploy → Zero downtime</em></p>
7 </div>9 </div>
8 10
9-## Why Gordon?11+## What is Gordon?
12+
13+Gordon is a lightweight deployment system that turns any VPS into a container hosting platform with push-to-deploy capabilities. It combines a private container registry with an intelligent reverse proxy to create a self-hosted alternative to complex orchestration systems.
10 14
11-**You have a $5 VPS. You want to run multiple apps. You don't want complex CI/CD pipelines, expensive PaaS solutions, or overcomplicated orchestration.**15+### Key Features
12 16
13-Gordon is the missing piece that makes container deployment on budget VPS servers as simple as expensive PaaS solutions. One binary, one config file, unlimited apps.17+- **Built-in Container Registry**: Your VPS becomes a private registry
18+- **Push-to-Deploy**: Pushing an image triggers automatic deployment
19+- **Smart Routing**: Multi-domain support with automatic HTTPS
20+- **Zero-Downtime Updates**: Graceful container swaps on new pushes
21+- **Network Isolation**: Each app gets its own network with attached services
22+- **Auto-Volume Management**: Persistent storage from Dockerfile VOLUME directives
23+- **Environment Merging**: Dockerfile ENV + your custom variables
24+- **Minimal Footprint**: ~15MB RAM usage, single binary
14 25
15 ### Perfect For26 ### Perfect For
16-- **Solo developers** with multiple side projects on one VPS27+
17-- **Small teams** who want Heroku-like simplicity without the cost28+- **Solo developers** running multiple projects on one VPS
18-- **Agencies** deploying client apps across VPS servers29+- **Small teams** wanting simple, reliable deployments
19-- **Anyone** tired of complex deployment pipelines30+- **Agencies** managing client applications across servers
31+- **Anyone** tired of overengineered deployment solutions
20 32
21 ## How It Works33 ## How It Works
22 34
@@ -29,96 +41,171 @@ podman run -p 8080:8080 myapp # Works? Great!
29 podman tag myapp registry.mydomain.com/myapp:latest41 podman tag myapp registry.mydomain.com/myapp:latest
30 podman push registry.mydomain.com/myapp:latest42 podman push registry.mydomain.com/myapp:latest
31 43
32-# 3. That's it. Gordon deploys it automatically.44+# 3. That's it. Gordon handles the rest.
33 ```45 ```
34 46
35-**No build servers. No CI/CD complexity. If it runs on your machine, it runs in production.**47+**Your machine is the build server. If it runs locally, it runs in production.**
48+
49+## Quick Start (5 minutes)
36 50
37-## Key Features51+### Prerequisites
52+- Ubuntu/Debian VPS with root access
53+- Domain pointing to your VPS
54+- Cloudflare account (free tier works)
38 55
39-- **Local-First Development**: Your machine is the build server56+### Installation
40-- **Built-in Container Registry**: Your VPS becomes a private registry
41-- **Push-to-Deploy**: New images deploy instantly
42-- **Multi-Domain Routing**: Unlimited apps with automatic HTTPS via Cloudflare
43-- **Zero-Downtime Updates**: Graceful container swaps
44-- **Network Isolation**: Each app gets its own isolated network with attached services (databases, caches, etc.)
45-- **Auto-Route Creation** (Optional): Push `myapp.mydomain.com:latest` → route created automatically
46-- **Auto-Volume Management**: Zero-config persistent storage from Dockerfile VOLUME directives
47-- **Auto-Environment Injection**: Dockerfile ENV directives automatically merge with your custom environment variables
48 57
49-## Quick Start (5 minutes)58+```bash
59+# Download Gordon from the releases page
60+# https://github.com/bnema/gordon/releases/latest
61+# Example: wget https://github.com/bnema/gordon/releases/download/v2.0.0/gordon_2.0.0_linux_amd64.tar.gz
62+# Then extract:
63+tar -xzf gordon_*.tar.gz
64+chmod +x gordon
65+sudo mv gordon /usr/local/bin/
66+
67+# Create initial config
68+gordon start
69+# Press Ctrl+C after config is created
70+```
71+
72+**Important:** This is just the binary installation. For a complete working setup including networking, firewall, and systemd service, follow the [detailed setup guide](#detailed-setup-guide) below.
73+
74+## Core Concepts
75+
76+### Local-First Development
77+Your dev machine likely has 8-16 cores and 16-32GB RAM. Your VPS has 1-2 cores and 1-4GB RAM. Why build containers on the weak machine? Gordon lets you build locally and deploy the finished product.
78+
79+### Push-to-Deploy Workflow
80+```bash
81+# Initial deployment
82+podman build -t myapp .
83+podman push registry.mydomain.com/myapp:latest
84+# Visit https://app.mydomain.com
85+
86+# Update deployment
87+podman build -t myapp .
88+podman push registry.mydomain.com/myapp:latest
89+# Zero-downtime update applied automatically
90+```
91+
92+### Automatic Volume Management
93+```dockerfile
94+# In your Dockerfile
95+FROM postgres:15
96+VOLUME ["/var/lib/postgresql/data"] # Gordon creates persistent storage
97+
98+FROM node:18
99+VOLUME ["/app/uploads", "/app/data"] # Multiple volumes supported
100+```
101+
102+### Environment Configuration
103+Gordon automatically creates and loads env files based on domain names:
104+```bash
105+# Auto-created on first run: ~/.local/share/gordon/env/app_mydomain_com.env
106+# Just add your variables:
107+DATABASE_URL=postgresql://localhost:5432/myapp
108+API_KEY=sk-1234567890
109+NODE_ENV=production
110+```
111+Supports secrets from `pass` and `SOPS`. See [examples/](examples/) for advanced usage.
112+
113+### Network Isolation
114+Each app runs in its own isolated network by default. Attach services for internal communication:
115+```toml
116+[attachments]
117+"app.mydomain.com" = ["my-postgres:latest", "my-redis:latest"]
118+# Access services via hostname: my-postgres:5432, my-redis:6379
119+```
120+Gordon also supports network groups, see [examples/](examples/) for advanced usage.
121+
122+### Persistent Storage
123+- **Volumes**: Automatically created from Dockerfile VOLUME directives, persist across updates
124+
125+### Production ready logging
126+- **Logs**: All container output saved to `~/.local/share/gordon/logs/containers/`
127+- **Rotation**: Logs rotate at 100MB, keeping 3 backups for 28 days
50 128
51-### 1. Setup VPS129+## Configuration
130+
131+Gordon uses a single TOML file for all configuration:
132+
133+```toml
134+# ~/.config/gordon/gordon.toml
135+[server]
136+registry_domain = "registry.mydomain.com" # Required
137+
138+[registry_auth]
139+username = "admin" # Choose your credentials
140+password = "your-secure-password"
141+
142+[routes]
143+"app.mydomain.com" = "myapp:latest" # Domain → Image mapping
144+"api.mydomain.com" = "myapi:v2.1.0" # Pin specific versions
145+
146+# Optional: Attach services to apps
147+[attachments]
148+"app.mydomain.com" = ["postgres:latest", "redis:latest"]
149+# Services accessible via internal DNS: postgres:5432, redis:6379
150+```
151+
152+See [examples/](examples/) for advanced configurations including network groups and more.
153+
154+## Detailed Setup Guide (Using podman in rootless mode)
155+Note: Refer to Quick Start for the binary installation.
156+
157+### 1. VPS Preparation
52 ```bash158 ```bash
53-# Install essentials on Ubuntu/Debian159+# Update system
54-sudo apt update && sudo apt install -y podman ufw160+sudo apt update && sudo apt upgrade -y
161+
162+# Install podman and firewall
163+sudo apt install -y podman ufw
55 164
56 # Configure firewall165 # Configure firewall
57-sudo ufw --force enable
58-sudo ufw default deny incoming
59-sudo ufw default allow outgoing
60 sudo ufw allow 22/tcp 80/tcp 443/tcp166 sudo ufw allow 22/tcp 80/tcp 443/tcp
61-sudo ufw enable167+sudo default deny
168+sudo ufw --force enable
62 ```169 ```
63 170
64-### 2. Redirect Ports171+### 2. Enable Port Forwarding
65 ```bash172 ```bash
66-# Redirect 80/443 to 8080173+# Forward 80/443 to unprivileged port 8080
67 sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080174 sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
68 sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8080175 sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8080
176+
177+# Make persistent
69 sudo apt install -y iptables-persistent178 sudo apt install -y iptables-persistent
70 sudo netfilter-persistent save179 sudo netfilter-persistent save
71 ```180 ```
72 181
73-### 3. Enable Rootless Containers182+### 3. Configure Rootless Containers
74 ```bash183 ```bash
184+# Enable user namespaces
75 echo 'user.max_user_namespaces=28633' | sudo tee -a /etc/sysctl.conf185 echo 'user.max_user_namespaces=28633' | sudo tee -a /etc/sysctl.conf
76 sudo sysctl -p186 sudo sysctl -p
187+
188+# Setup subuid/subgid
77 sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER189 sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
190+
191+# Enable podman socket
78 systemctl --user enable --now podman.socket192 systemctl --user enable --now podman.socket
79 ```193 ```
80 194
81-### 4. Configure Registry195+### 4. Registry Configuration
82 ```bash196 ```bash
83 mkdir -p ~/.config/containers197 mkdir -p ~/.config/containers
84-tee ~/.config/containers/registries.conf > /dev/null <<EOF198+cat > ~/.config/containers/registries.conf <<EOF
85-[registries.search]
86-registries = ['docker.io', 'registry.mydomain.com']
87-
88 [registries.insecure]199 [registries.insecure]
89-registries = ['registry.mydomain.com']200+registries = ['localhost:5000']
90 EOF201 EOF
91 ```202 ```
92 203
93-### 5. Install Gordon204+### 5. Create Systemd Service
94-```bash
95-wget https://github.com/bnema/gordon/releases/latest/download/gordon-linux-amd64
96-chmod +x gordon-linux-amd64
97-sudo mv gordon-linux-amd64 /usr/local/bin/gordon
98-```
99-
100-### 6. Configure Gordon
101-```bash
102-# Gordon creates a default config file on first run
103-# Edit it to set your registry domain and credentials:
104-# gordon.toml location: ./ → ~/.config/gordon/ → ~/.gordon/ → ~/ → /etc/gordon/
105-
106-# Key settings to update:
107-# - registry_domain = "registry.mydomain.com"
108-# - registry_auth username/password
109-# - routes for your domains
110-```
111-
112-### 7. Point DNS
113-```
114-A *.mydomain.com → YOUR_VPS_IP
115-A mydomain.com → YOUR_VPS_IP
116-```
117-
118-### 8. Create Service
119 ```bash205 ```bash
206+# Create user service
120 mkdir -p ~/.config/systemd/user207 mkdir -p ~/.config/systemd/user
121-tee ~/.config/systemd/user/gordon.service > /dev/null <<EOF208+cat > ~/.config/systemd/user/gordon.service <<EOF
122 [Unit]209 [Unit]
123 Description=Gordon Container Platform210 Description=Gordon Container Platform
124 After=podman.socket211 After=podman.socket
@@ -127,7 +214,6 @@ After=podman.socket
127 Type=simple214 Type=simple
128 Restart=always215 Restart=always
129 RestartSec=5216 RestartSec=5
130-Environment=CONTAINER_HOST=unix://%t/podman/podman.sock
131 ExecStart=/usr/local/bin/gordon start217 ExecStart=/usr/local/bin/gordon start
132 WorkingDirectory=%h218 WorkingDirectory=%h
133 219
@@ -135,304 +221,72 @@ WorkingDirectory=%h
135 WantedBy=default.target221 WantedBy=default.target
136 EOF222 EOF
137 223
224+# Enable and start service
138 systemctl --user daemon-reload225 systemctl --user daemon-reload
139 systemctl --user enable --now gordon226 systemctl --user enable --now gordon
140 sudo loginctl enable-linger $USER227 sudo loginctl enable-linger $USER
141-```
142-
143-### 9. Deploy Your First App
144-```bash
145-# From your dev machine:
146-podman login registry.mydomain.com
147-# (Use the credentials from your gordon.toml)
148 228
149-podman tag myapp:latest registry.mydomain.com/myapp:latest229+# Verify it's running
150-podman push registry.mydomain.com/myapp:latest230+systemctl --user status gordon
151-# Visit https://app.mydomain.com
152 ```231 ```
153 232
154-## Real-World Examples233+## Deployment Strategies
155 234
156-### Push-to-Deploy Workflow235+### Simple Deployment
157 ```bash236 ```bash
158 # Build locally237 # Build locally
159 podman build -t myapp .238 podman build -t myapp .
160 239
161-# Push latest = instant deploy240+# Test locally
162-podman push registry.mydomain.com/myapp:latest241+podman run -p 8080:8080 myapp
163-```
164-
165-### Version Control & Rollbacks
166 242
167-Gordon supports multiple rollback strategies. Choose the approach that fits your workflow:243+# Push to registry
244+podman tag myapp registry.mydomain.com/myapp:latest
245+podman push registry.mydomain.com/myapp:latest
168 246
169-#### Method 1: Tag-Based Rollbacks (Simple)247+# Profit!
170-```bash
171-# Tag and push a specific version
172-podman tag myapp:v1.0.1 registry.mydomain.com/myapp:v1.0.1
173-podman push registry.mydomain.com/myapp:v1.0.1
174-
175-# Tag and push latest (v1.0.2 in this example)
176-podman tag myapp:v1.0.2 registry.mydomain.com/myapp:v1.0.2 && podman tag myapp:v1.0.2 registry.mydomain.com/myapp:latest
177-podman push registry.mydomain.com/myapp:v1.0.2 && podman push registry.mydomain.com/myapp:latest
178-
179-# Rollback: Update gordon.toml and reload
180-# From: "app.mydomain.com" = "myapp:latest"
181-# To: "app.mydomain.com" = "myapp:v1.0.1"
182-# Then: systemctl --user reload gordon
183 ```248 ```
184 249
185-#### Method 2: Manifest Annotation Deployment (Advanced)250+### Versioned Deployment
186 ```bash251 ```bash
187-# Build and push your versioned images first252+# Deploy specific version
188-export VERSION=v1.0.1253+podman tag myapp:v1.2.0 registry.mydomain.com/myapp:v1.2.0
189-podman build --tag myapp:$VERSION --tag registry.mydomain.com/myapp:$VERSION .254+podman push registry.mydomain.com/myapp:v1.2.0
190-podman push registry.mydomain.com/myapp:$VERSION
191 255
192-export VERSION=v1.0.2256+# Update route in gordon.toml
193-podman build --tag myapp:$VERSION --tag registry.mydomain.com/myapp:$VERSION .257+# "app.mydomain.com" = "myapp:v1.2.0"
194-podman push registry.mydomain.com/myapp:$VERSION258+```
195 259
196-# Deploy v1.0.2 with version annotation260+### Advanced: Manifest Annotations
197-export VERSION=v1.0.2261+```bash
262+# Deploy with metadata
263+export VERSION=v1.2.0
198 podman manifest create myapp:latest264 podman manifest create myapp:latest
199 podman manifest add myapp:latest registry.mydomain.com/myapp:$VERSION265 podman manifest add myapp:latest registry.mydomain.com/myapp:$VERSION
200 podman manifest annotate myapp:latest --annotation version=$VERSION registry.mydomain.com/myapp:$VERSION266 podman manifest annotate myapp:latest --annotation version=$VERSION registry.mydomain.com/myapp:$VERSION
201 podman manifest push myapp:latest registry.mydomain.com/myapp:latest267 podman manifest push myapp:latest registry.mydomain.com/myapp:latest
202-
203-# Rollback to v1.0.1 (just change the version)
204-export VERSION=v1.0.1
205-podman manifest create myapp:latest --amend
206-podman manifest add myapp:latest registry.mydomain.com/myapp:$VERSION
207-podman manifest annotate myapp:latest --annotation version=$VERSION registry.mydomain.com/myapp:$VERSION
208-podman manifest push myapp:latest registry.mydomain.com/myapp:latest
209-# Gordon automatically deploys the specified version
210-```
211-
212-**Recommendation**: Use Method 2 for instant deployments without config file edits. Method 1 is best for simple workflows.
213-
214-### Auto-Route Creation
215-```toml
216-[auto_route]
217-enabled = true
218-```
219-
220-```bash
221-# Image name becomes route automatically
222-podman push registry.mydomain.com/staging.example.com:latest
223-# Creates: "staging.example.com" = "staging.example.com:latest"
224 ```268 ```
225 269
226-## Persistent Data with Volumes270+## Community
227-
228-Gordon automatically detects `VOLUME` directives in your Dockerfiles and creates persistent named volumes for your containers. **Zero configuration required** - just add `VOLUME` instructions to your Dockerfile.
229-
230-### How It Works
231 271
232-```dockerfile272+Gordon is open source and welcomes contributions:
233-# In your Dockerfile273+- [Report bugs](https://github.com/bnema/gordon/issues)
234-FROM node:18-alpine274+- [Suggest features](https://github.com/bnema/gordon/discussions)
235-WORKDIR /app275+- [Submit PRs](https://github.com/bnema/gordon/pulls)
236-COPY package*.json ./
237-RUN npm install
238-COPY . .
239-
240-# Gordon automatically creates volumes for these paths
241-VOLUME ["/app/data", "/app/uploads"]
242-
243-EXPOSE 3000
244-CMD ["npm", "start"]
245-```
246-
247-When you deploy this image, Gordon will:
248-1. **Detect** the `VOLUME ["/app/data", "/app/uploads"]` directive
249-2. **Create** named volumes like `gordon-app-example-com-a1b2c3d4` (one per path)
250-3. **Mount** them to your container automatically
251-4. **Preserve** data across container restarts and updates
252-
253-### Volume Features
254-
255-- **Automatic Detection**: No config needed - Gordon reads VOLUME directives from your images
256-- **Persistent Storage**: Data survives container restarts, updates, and VPS reboots
257-- **Zero Downtime**: Volumes re-attach instantly when containers restart
258-- **Predictable Naming**: `gordon-{domain}-{path-hash}` format for easy identification
259-- **Cross-Platform**: Works with Docker and Podman
260-
261-### Example Workflows
262-
263-#### Database Container
264-```dockerfile
265-FROM postgres:15
266-# Gordon auto-creates persistent volume for PostgreSQL data
267-VOLUME ["/var/lib/postgresql/data"]
268-```
269-
270-#### Web App with File Uploads
271-```dockerfile
272-FROM nginx:alpine
273-COPY dist/ /usr/share/nginx/html/
274-# Gordon auto-creates persistent volume for user uploads
275-VOLUME ["/usr/share/nginx/html/uploads"]
276-```
277-
278-#### Multi-Volume Application
279-```dockerfile
280-FROM myapp:latest
281-# Gordon creates separate volumes for each path
282-VOLUME ["/app/data", "/app/logs", "/app/cache"]
283-```
284-
285-### Volume Configuration (Optional)
286-
287-```toml
288-[volumes]
289-auto_create = true # Default: true (set to false to disable)
290-prefix = "gordon" # Default: "gordon" (volume name prefix)
291-preserve = true # Default: true (keep volumes when removing containers)
292-```
293-
294-**Most users never need to touch this configuration** - the defaults work perfectly for 99% of use cases.
295-
296-## Smart Environment & Volume Management
297-
298-Gordon automatically handles both environment variables and persistent storage with zero configuration:
299-
300-### Environment Variables
301-- **Auto-Detection**: Reads `ENV` directives from your Dockerfile
302-- **Smart Merging**: Your `.env` files override Dockerfile ENV values
303-- **Secret Integration**: Supports `pass` and `SOPS` for sensitive data
304-
305-### Persistent Volumes
306-- **Auto-Detection**: Reads `VOLUME` directives from your Dockerfile
307-- **Zero Configuration**: Creates and manages volumes automatically
308-- **Data Persistence**: Survives container updates and reboots
309-
310-See [examples/](examples/) for detailed configuration examples.
311-
312-## Advanced Configuration
313-
314-### Full Config Structure (with default values)
315-```toml
316-# gordon.toml - Auto-generated on first run
317-[server]
318-port = 8080 # Default: 8080
319-registry_port = 5000 # Default: 5000
320-registry_domain = "registry.mydomain.com" # No default (required)
321-runtime = "auto" # Default: "auto" (auto, docker, podman, podman-rootless)
322-socket_path = "" # Default: "" (auto-detected)
323-data_dir = "~/.local/share/gordon" # Default: ~/.local/share/gordon (or ./data for root)
324-ssl_email = "" # No default (optional)
325-# runtime auto-detects: docker → podman-rootless → podman
326-# Override with env: CONTAINER_HOST=unix:///custom/socket
327-
328-[registry_auth]
329-enabled = true # Default: true
330-username = "admin" # No default (required when enabled)
331-password = "your-secure-password" # No default (required when enabled)
332-
333-[routes]
334-"app.mydomain.com" = "myapp:latest" # Default: {} (empty map)
335-# Routes default to HTTPS unless prefixed with http://
336-
337-# Network isolation - each app gets its own isolated network
338-[attachments]
339-"app.mydomain.com" = ["my-postgres:latest", "my-redis:latest"]
340-# Services accessible via: postgres:5432, redis:6379 (internal DNS)
341-# Create custom Dockerfiles with VOLUME directives for persistence
342-
343-[auto_route]
344-enabled = false # Default: false
345-
346-[volumes]
347-auto_create = true # Default: true (automatically create volumes from VOLUME directives)
348-prefix = "gordon" # Default: "gordon" (volume name prefix)
349-preserve = true # Default: true (keep volumes when containers are removed)
350-# Volumes are auto-created from Dockerfile VOLUME directives
351-# Naming: gordon-{domain}-{path-hash} (e.g., gordon-app-example-com-a1b2c3d4)
352-
353-[env]
354-dir = "{data_dir}/env" # Default: {data_dir}/env
355-providers = ["pass", "sops"] # Default: ["pass", "sops"]
356-# Example env file: ./data/env/app_mydomain_com.env
357-# NODE_ENV=production
358-# DATABASE_URL=postgresql://localhost:5432/prod
359-# API_KEY=${pass:company/api-key} # Unix pass integration
360-# JWT_SECRET=${sops:secrets.yaml:jwt} # SOPS integration
361-
362-[logging]
363-enabled = true # Default: true
364-level = "info" # Default: "info" (debug, info, warn, error, fatal, panic)
365-dir = "{data_dir}/logs" # Default: {data_dir}/logs
366-main_log_file = "gordon.log" # Default: "gordon.log"
367-proxy_log_file = "proxy.log" # Default: "proxy.log"
368-container_log_dir = "containers" # Default: "containers"
369-max_size = 100 # Default: 100 (MB before rotation)
370-max_backups = 3 # Default: 3 (old files to keep)
371-max_age = 28 # Default: 28 (days to keep old files)
372-compress = true # Default: true (gzip rotated logs)
373-# Creates: gordon.log, proxy.log, containers/*.log
374-```
375-
376-## FAQ
377-
378-**Q: How is this different from Traefik/Nginx Proxy Manager?**
379-A: Those are just reverse proxies. Gordon is a complete deployment platform with built-in registry, automatic deployment, and container lifecycle management.
380-
381-**Q: Where do builds happen?**
382-A: On YOUR machine. If it runs locally, it runs in production.
383-
384-**Q: How do I rollback?**
385-A: Change the version annotation in your manifest and push, or edit gordon.toml. Takes seconds.
386-
387-**Q: Do I need Cloudflare?**
388-A: Yes, for SSL certificates and DDoS protection.
389-
390-**Q: Resource requirements?**
391-A: Runs on 1GB RAM VPS. Gordon uses <15MB RAM.
392-
393-**Q: How do I check if volumes are working?**
394-A: Use `docker volume ls` or `podman volume ls` to see Gordon-managed volumes (named `gordon-*`).
395-
396-**Q: Can I disable automatic volumes?**
397-A: Yes, set `volumes.auto_create = false` in your gordon.toml config.
398-
399-**Q: What happens to my data when I update containers?**
400-A: Volumes are preserved by default. Your data persists across updates and reboots.
401-
402-**Q: How do I backup volume data?**
403-A: Volumes are regular Docker/Podman volumes - use standard backup tools like `docker run --rm -v volume_name:/data -v $(pwd):/backup alpine tar czf /backup/backup.tar.gz -C /data .`
404-
405-**Q: Do Dockerfile ENV directives work automatically?**
406-A: Yes! Gordon reads ENV directives from your images and merges them with your custom .env files.
407-
408-**Q: What happens if I set the same environment variable in both Dockerfile and .env?**
409-A: Your .env file always wins - custom environment variables override Dockerfile ENV directives.
410-
411-**Q: Can I see what environment variables are being applied?**
412-A: Yes, check Gordon's logs - it shows both Dockerfile ENV and final merged environment variables.
413-
414-**Q: Do I need to rebuild my images to change environment variables?**
415-A: No! Just update your .env files and reload Gordon. Dockerfile ENV provides defaults; .env provides overrides.
416 276
417 ## Philosophy277 ## Philosophy
418 278
419-```bash279+Traditional deployment pipelines recreate your development environment in CI/CD systems, adding complexity and points of failure. Gordon takes a different approach: **your development machine is already a perfect build environment**.
420-# Traditional CI/CD
421-push code → wait for build → hope it works → debug remotely → repeat
422 280
423-# The Gordon Way281+```bash
424-build locally → test locally → push image → instant deploy282+# Traditional: Code → CI/CD → Build → Test → Deploy → Hope
283+# Gordon: Code → Build → Test → Push → Done
425 ```284 ```
426 285
427-Your machine already works. Why replicate it in CI/CD?
428-
429 ## License286 ## License
430 287
431 GPL-3.0 - Use freely, contribute back.288 GPL-3.0 - Use freely, contribute back.
432 289
433 ---290 ---
434 291
435-<div align="center">292+**Built for developers who ship.** If Gordon helps you deploy faster, [give it a star](https://github.com/bnema/gordon).⏎̸
436- <p><strong>Stop overcomplicating container deployment.</strong></p>
437- <p>Star this repo if Gordon saves you money!</p>
438-</div>⏎̸
M examples/README.md +65 -1
@@ -1,7 +1,71 @@
1-# Gordon v2 Configuration Examples
1+# Gordon Configuration Examples
22
33 This directory contains configuration examples for different use cases and environments.
44
5+## Full Configuration Reference
6+
7+Here's the complete Gordon configuration with all default values:
8+
9+```toml
10+# gordon.toml - Complete configuration reference with defaults
11+
12+[server]
13+port = 8080 # HTTP/HTTPS proxy port (default: 8080)
14+registry_port = 5000 # Container registry port (default: 5000)
15+registry_domain = "" # REQUIRED - Your registry domain (no default)
16+runtime = "auto" # Container runtime (default: "auto")
17+ # Options: auto, docker, podman, podman-rootless
18+socket_path = "" # Custom socket path (default: "" - auto-detected)
19+data_dir = "~/.local/share/gordon" # Data directory (default: ~/.local/share/gordon)
20+ # Root user default: ./data
21+ssl_email = "" # Email for Let's Encrypt (default: "" - uses Cloudflare)
22+
23+[registry_auth]
24+enabled = true # Enable registry authentication (default: true)
25+username = "" # REQUIRED when enabled (no default)
26+password = "" # REQUIRED when enabled (no default)
27+
28+[routes]
29+# Domain to image mappings (default: empty)
30+# "app.example.com" = "myapp:latest"
31+# "api.example.com" = "myapi:v1.0.0"
32+
33+[network_groups]
34+# Group domains for shared networking (default: empty)
35+# "backend" = ["app.example.com", "api.example.com"]
36+# "monitoring" = ["grafana.example.com", "prometheus.example.com"]
37+
38+[attachments]
39+# Attach services to apps or groups (default: empty)
40+# "app.example.com" = ["postgres:latest", "redis:latest"]
41+# "backend" = ["shared-cache:latest", "message-queue:latest"]
42+
43+[auto_route]
44+enabled = false # Auto-create routes from image names (default: false)
45+
46+[volumes]
47+auto_create = true # Auto-create volumes from VOLUME directives (default: true)
48+prefix = "gordon" # Volume name prefix (default: "gordon")
49+preserve = true # Keep volumes when removing containers (default: true)
50+
51+[env]
52+dir = "{data_dir}/env" # Environment files directory (default: {data_dir}/env)
53+providers = ["pass", "sops"] # Secret providers (default: ["pass", "sops"])
54+
55+[logging]
56+enabled = true # Enable file logging (default: true)
57+level = "info" # Log level (default: "info")
58+ # Options: trace, debug, info, warn, error, fatal, panic
59+dir = "{data_dir}/logs" # Log directory (default: {data_dir}/logs)
60+main_log_file = "gordon.log" # Main log file (default: "gordon.log")
61+proxy_log_file = "proxy.log" # Proxy log file (default: "proxy.log")
62+container_log_dir = "containers" # Container logs subdirectory (default: "containers")
63+max_size = 100 # Max log size in MB (default: 100)
64+max_backups = 3 # Number of old logs to keep (default: 3)
65+max_age = 28 # Days to keep old logs (default: 28)
66+compress = true # Compress rotated logs (default: true)
67+```
68+
569 ## 📁 Available Examples
670
771 ### 🚀 [`minimal.toml`](minimal.toml)
@@ -1,7 +1,71 @@
1-# Gordon v2 Configuration Examples1+# Gordon Configuration Examples
2 2
3 This directory contains configuration examples for different use cases and environments.3 This directory contains configuration examples for different use cases and environments.
4 4
5+## Full Configuration Reference
6+
7+Here's the complete Gordon configuration with all default values:
8+
9+```toml
10+# gordon.toml - Complete configuration reference with defaults
11+
12+[server]
13+port = 8080 # HTTP/HTTPS proxy port (default: 8080)
14+registry_port = 5000 # Container registry port (default: 5000)
15+registry_domain = "" # REQUIRED - Your registry domain (no default)
16+runtime = "auto" # Container runtime (default: "auto")
17+ # Options: auto, docker, podman, podman-rootless
18+socket_path = "" # Custom socket path (default: "" - auto-detected)
19+data_dir = "~/.local/share/gordon" # Data directory (default: ~/.local/share/gordon)
20+ # Root user default: ./data
21+ssl_email = "" # Email for Let's Encrypt (default: "" - uses Cloudflare)
22+
23+[registry_auth]
24+enabled = true # Enable registry authentication (default: true)
25+username = "" # REQUIRED when enabled (no default)
26+password = "" # REQUIRED when enabled (no default)
27+
28+[routes]
29+# Domain to image mappings (default: empty)
30+# "app.example.com" = "myapp:latest"
31+# "api.example.com" = "myapi:v1.0.0"
32+
33+[network_groups]
34+# Group domains for shared networking (default: empty)
35+# "backend" = ["app.example.com", "api.example.com"]
36+# "monitoring" = ["grafana.example.com", "prometheus.example.com"]
37+
38+[attachments]
39+# Attach services to apps or groups (default: empty)
40+# "app.example.com" = ["postgres:latest", "redis:latest"]
41+# "backend" = ["shared-cache:latest", "message-queue:latest"]
42+
43+[auto_route]
44+enabled = false # Auto-create routes from image names (default: false)
45+
46+[volumes]
47+auto_create = true # Auto-create volumes from VOLUME directives (default: true)
48+prefix = "gordon" # Volume name prefix (default: "gordon")
49+preserve = true # Keep volumes when removing containers (default: true)
50+
51+[env]
52+dir = "{data_dir}/env" # Environment files directory (default: {data_dir}/env)
53+providers = ["pass", "sops"] # Secret providers (default: ["pass", "sops"])
54+
55+[logging]
56+enabled = true # Enable file logging (default: true)
57+level = "info" # Log level (default: "info")
58+ # Options: trace, debug, info, warn, error, fatal, panic
59+dir = "{data_dir}/logs" # Log directory (default: {data_dir}/logs)
60+main_log_file = "gordon.log" # Main log file (default: "gordon.log")
61+proxy_log_file = "proxy.log" # Proxy log file (default: "proxy.log")
62+container_log_dir = "containers" # Container logs subdirectory (default: "containers")
63+max_size = 100 # Max log size in MB (default: 100)
64+max_backups = 3 # Number of old logs to keep (default: 3)
65+max_age = 28 # Days to keep old logs (default: 28)
66+compress = true # Compress rotated logs (default: true)
67+```
68+
5 ## 📁 Available Examples69 ## 📁 Available Examples
6 70
7 ### 🚀 [`minimal.toml`](minimal.toml)71 ### 🚀 [`minimal.toml`](minimal.toml)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close