docs(config): document standalone l4 services
- commit
- 7b54de4175e1113a2cd9800dc7ab2103962c1493
- author
- brice · · 2026-06-30 15:55 UTC
- parent
- 3c7cade
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
7 files · +213 -3
M docs/cli/traffic.md +3 -0
| @@ -15,6 +15,8 @@ gordon traffic status --remote prod | |||
| 15 | 15 | gordon traffic status --remote https://gordon.example.com --json | |
| 16 | 16 | ``` | |
| 17 | 17 | ||
| 18 | + | Remote mode queries the running Gordon admin API. Local mode does not synthesize runtime traffic state from a fresh config load; use a configured remote target for authoritative counters and reload status. | |
| 19 | + | ||
| 18 | 20 | ## Flags | |
| 19 | 21 | ||
| 20 | 22 | | Flag | Description | | |
| @@ -87,4 +89,5 @@ gordon traffic status --remote https://gordon.example.com --json | |||
| 87 | 89 | ## Related | |
| 88 | 90 | ||
| 89 | 91 | - [Traffic configuration](../config/traffic.md) | |
| 92 | + | - [Standalone services](../config/services.md) | |
| 90 | 93 | - [Server status](./status.md) | |
| @@ -15,6 +15,8 @@ gordon traffic status --remote prod | |||||
| 15 | gordon traffic status --remote https://gordon.example.com --json | 15 | gordon traffic status --remote https://gordon.example.com --json | ||
| 16 | ``` | 16 | ``` | ||
| 17 | 17 | ||||
| 18 | + | Remote mode queries the running Gordon admin API. Local mode does not synthesize runtime traffic state from a fresh config load; use a configured remote target for authoritative counters and reload status. | |||
| 19 | + | ||||
| 18 | ## Flags | 20 | ## Flags | ||
| 19 | 21 | ||||
| 20 | | Flag | Description | | 22 | | Flag | Description | | ||
| @@ -87,4 +89,5 @@ gordon traffic status --remote https://gordon.example.com --json | |||||
| 87 | ## Related | 89 | ## Related | ||
| 88 | 90 | ||||
| 89 | - [Traffic configuration](../config/traffic.md) | 91 | - [Traffic configuration](../config/traffic.md) | ||
| 92 | + | - [Standalone services](../config/services.md) | |||
| 90 | - [Server status](./status.md) | 93 | - [Server status](./status.md) | ||
M docs/config/index.md +1 -0
| @@ -289,6 +289,7 @@ Pattern: `GORDON_SECTION_KEY` (uppercase, underscores instead of dots) | |||
| 289 | 289 | - [Server Configuration](./server.md) | |
| 290 | 290 | - [Routes Configuration](./routes.md) | |
| 291 | 291 | - [External Routes](./external-routes.md) | |
| 292 | + | - [Standalone Services](./services.md) | |
| 292 | 293 | - [Traffic Plane](./traffic.md) | |
| 293 | 294 | - [Authentication](./auth.md) | |
| 294 | 295 | - [Telemetry](./telemetry.md) | |
| @@ -289,6 +289,7 @@ Pattern: `GORDON_SECTION_KEY` (uppercase, underscores instead of dots) | |||||
| 289 | - [Server Configuration](./server.md) | 289 | - [Server Configuration](./server.md) | ||
| 290 | - [Routes Configuration](./routes.md) | 290 | - [Routes Configuration](./routes.md) | ||
| 291 | - [External Routes](./external-routes.md) | 291 | - [External Routes](./external-routes.md) | ||
| 292 | + | - [Standalone Services](./services.md) | |||
| 292 | - [Traffic Plane](./traffic.md) | 293 | - [Traffic Plane](./traffic.md) | ||
| 293 | - [Authentication](./auth.md) | 294 | - [Authentication](./auth.md) | ||
| 294 | - [Telemetry](./telemetry.md) | 295 | - [Telemetry](./telemetry.md) | ||
M docs/config/reference.md +44 -0
| @@ -173,6 +173,28 @@ preserve = true # Keep volumes when containers are | |||
| 173 | 173 | [external_routes] | |
| 174 | 174 | # "domain.com" = "host:port" # Proxy to non-container services | |
| 175 | 175 | ||
| 176 | + | # ============================================================================= | |
| 177 | + | # STANDALONE SERVICES | |
| 178 | + | # ============================================================================= | |
| 179 | + | # [[services]] | |
| 180 | + | # name = "rust" | |
| 181 | + | # image = "registry.example.com:5000/rust:latest" | |
| 182 | + | # enabled = true | |
| 183 | + | # env_file = "/srv/gordon/services/rust.env" | |
| 184 | + | # | |
| 185 | + | # [[services.ports]] | |
| 186 | + | # name = "game" | |
| 187 | + | # container = 28015 | |
| 188 | + | # protocol = "udp" | |
| 189 | + | # publish = "127.0.0.1:38015" | |
| 190 | + | # | |
| 191 | + | # [[services.ports]] | |
| 192 | + | # name = "rcon" | |
| 193 | + | # container = 28016 | |
| 194 | + | # protocol = "tcp" | |
| 195 | + | # publish = "127.0.0.1:38016" | |
| 196 | + | # trusted_cidrs = ["100.64.0.0/10"] | |
| 197 | + | ||
| 176 | 198 | # ============================================================================= | |
| 177 | 199 | # NETWORK GROUPS | |
| 178 | 200 | # ============================================================================= | |
| @@ -292,6 +314,27 @@ keep_last = 3 # Keep N newest tags per repository | |||
| 292 | 314 | | `volumes.auto_create` | `true` | Auto-create volumes | | |
| 293 | 315 | | `volumes.prefix` | `"gordon"` | Volume prefix | | |
| 294 | 316 | | `volumes.preserve` | `true` | Keep volumes | | |
| 317 | + | | `services[].name` | none | Standalone service name used by `service:<name>:<port>` traffic refs | | |
| 318 | + | | `services[].image` | none | Container image for enabled standalone services | | |
| 319 | + | | `services[].enabled` | `false` | Whether Gordon creates, starts, and reconciles the service container | | |
| 320 | + | | `services[].env` | `[]` | Inline `KEY=value` environment entries | | |
| 321 | + | | `services[].env_file` | `""` | Env file loaded before inline entries | | |
| 322 | + | | `services[].ports[].name` | none | Port name used by traffic service refs | | |
| 323 | + | | `services[].ports[].container` | none | Container port number | | |
| 324 | + | | `services[].ports[].protocol` | none | `tcp` or `udp` | | |
| 325 | + | | `services[].ports[].publish` | `""` | Host-side publish address, usually loopback for traffic-manager reachability | | |
| 326 | + | | `services[].ports[].private` | `false` | Require matching service and entrypoint `trusted_cidrs` for this port | | |
| 327 | + | | `services[].ports[].public` | `false` | Explicit public opt-out for admin port names such as `rcon` | | |
| 328 | + | | `services[].ports[].trusted_cidrs` | `[]` | CIDRs allowed for private port routing; must match the target entrypoint | | |
| 329 | + | | `services[].volumes[].source` | `""` | Explicit named volume or bind source; omitted service volumes use image `VOLUME` metadata | | |
| 330 | + | | `services[].volumes[].target` | none | Absolute container mount path | | |
| 331 | + | | `services[].volumes[].read_only` | `false` | Mount explicit volume read-only | | |
| 332 | + | | `services[].readiness.type` | `"none"` | `none`, `tcp`, or `log` | | |
| 333 | + | | `services[].readiness.path` | `""` | Log readiness path inside the container | | |
| 334 | + | | `services[].readiness.contains` | `""` | Text required in the readiness log | | |
| 335 | + | | `services[].readiness.timeout` | default wait | Positive readiness timeout when set | | |
| 336 | + | | `services[].cleanup.preserve_volumes` | `true` | Preserve managed image-discovered volumes on cleanup | | |
| 337 | + | | `services[].cleanup.remove_container` | `true` | Remove old, disabled, or removed service containers | | |
| 295 | 338 | | `backups.enabled` | `false` | Backup service disabled | | |
| 296 | 339 | | `backups.schedule` | `"daily"` | Backup scheduler preset | | |
| 297 | 340 | | `backups.storage_dir` | `""` | Uses `{server.data_dir}/backups` when empty | | |
| @@ -377,4 +420,5 @@ gordon serve | |||
| 377 | 420 | - [Telemetry](./telemetry.md) | |
| 378 | 421 | - [Network Isolation](./network-isolation.md) | |
| 379 | 422 | - [Volumes](./volumes.md) | |
| 423 | + | - [Standalone Services](./services.md) | |
| 380 | 424 | - [Images](./images.md) | |
| @@ -173,6 +173,28 @@ preserve = true # Keep volumes when containers are | |||||
| 173 | [external_routes] | 173 | [external_routes] | ||
| 174 | # "domain.com" = "host:port" # Proxy to non-container services | 174 | # "domain.com" = "host:port" # Proxy to non-container services | ||
| 175 | 175 | ||||
| 176 | + | # ============================================================================= | |||
| 177 | + | # STANDALONE SERVICES | |||
| 178 | + | # ============================================================================= | |||
| 179 | + | # [[services]] | |||
| 180 | + | # name = "rust" | |||
| 181 | + | # image = "registry.example.com:5000/rust:latest" | |||
| 182 | + | # enabled = true | |||
| 183 | + | # env_file = "/srv/gordon/services/rust.env" | |||
| 184 | + | # | |||
| 185 | + | # [[services.ports]] | |||
| 186 | + | # name = "game" | |||
| 187 | + | # container = 28015 | |||
| 188 | + | # protocol = "udp" | |||
| 189 | + | # publish = "127.0.0.1:38015" | |||
| 190 | + | # | |||
| 191 | + | # [[services.ports]] | |||
| 192 | + | # name = "rcon" | |||
| 193 | + | # container = 28016 | |||
| 194 | + | # protocol = "tcp" | |||
| 195 | + | # publish = "127.0.0.1:38016" | |||
| 196 | + | # trusted_cidrs = ["100.64.0.0/10"] | |||
| 197 | + | ||||
| 176 | # ============================================================================= | 198 | # ============================================================================= | ||
| 177 | # NETWORK GROUPS | 199 | # NETWORK GROUPS | ||
| 178 | # ============================================================================= | 200 | # ============================================================================= | ||
| @@ -292,6 +314,27 @@ keep_last = 3 # Keep N newest tags per repository | |||||
| 292 | | `volumes.auto_create` | `true` | Auto-create volumes | | 314 | | `volumes.auto_create` | `true` | Auto-create volumes | | ||
| 293 | | `volumes.prefix` | `"gordon"` | Volume prefix | | 315 | | `volumes.prefix` | `"gordon"` | Volume prefix | | ||
| 294 | | `volumes.preserve` | `true` | Keep volumes | | 316 | | `volumes.preserve` | `true` | Keep volumes | | ||
| 317 | + | | `services[].name` | none | Standalone service name used by `service:<name>:<port>` traffic refs | | |||
| 318 | + | | `services[].image` | none | Container image for enabled standalone services | | |||
| 319 | + | | `services[].enabled` | `false` | Whether Gordon creates, starts, and reconciles the service container | | |||
| 320 | + | | `services[].env` | `[]` | Inline `KEY=value` environment entries | | |||
| 321 | + | | `services[].env_file` | `""` | Env file loaded before inline entries | | |||
| 322 | + | | `services[].ports[].name` | none | Port name used by traffic service refs | | |||
| 323 | + | | `services[].ports[].container` | none | Container port number | | |||
| 324 | + | | `services[].ports[].protocol` | none | `tcp` or `udp` | | |||
| 325 | + | | `services[].ports[].publish` | `""` | Host-side publish address, usually loopback for traffic-manager reachability | | |||
| 326 | + | | `services[].ports[].private` | `false` | Require matching service and entrypoint `trusted_cidrs` for this port | | |||
| 327 | + | | `services[].ports[].public` | `false` | Explicit public opt-out for admin port names such as `rcon` | | |||
| 328 | + | | `services[].ports[].trusted_cidrs` | `[]` | CIDRs allowed for private port routing; must match the target entrypoint | | |||
| 329 | + | | `services[].volumes[].source` | `""` | Explicit named volume or bind source; omitted service volumes use image `VOLUME` metadata | | |||
| 330 | + | | `services[].volumes[].target` | none | Absolute container mount path | | |||
| 331 | + | | `services[].volumes[].read_only` | `false` | Mount explicit volume read-only | | |||
| 332 | + | | `services[].readiness.type` | `"none"` | `none`, `tcp`, or `log` | | |||
| 333 | + | | `services[].readiness.path` | `""` | Log readiness path inside the container | | |||
| 334 | + | | `services[].readiness.contains` | `""` | Text required in the readiness log | | |||
| 335 | + | | `services[].readiness.timeout` | default wait | Positive readiness timeout when set | | |||
| 336 | + | | `services[].cleanup.preserve_volumes` | `true` | Preserve managed image-discovered volumes on cleanup | | |||
| 337 | + | | `services[].cleanup.remove_container` | `true` | Remove old, disabled, or removed service containers | | |||
| 295 | | `backups.enabled` | `false` | Backup service disabled | | 338 | | `backups.enabled` | `false` | Backup service disabled | | ||
| 296 | | `backups.schedule` | `"daily"` | Backup scheduler preset | | 339 | | `backups.schedule` | `"daily"` | Backup scheduler preset | | ||
| 297 | | `backups.storage_dir` | `""` | Uses `{server.data_dir}/backups` when empty | | 340 | | `backups.storage_dir` | `""` | Uses `{server.data_dir}/backups` when empty | | ||
| @@ -377,4 +420,5 @@ gordon serve | |||||
| 377 | - [Telemetry](./telemetry.md) | 420 | - [Telemetry](./telemetry.md) | ||
| 378 | - [Network Isolation](./network-isolation.md) | 421 | - [Network Isolation](./network-isolation.md) | ||
| 379 | - [Volumes](./volumes.md) | 422 | - [Volumes](./volumes.md) | ||
| 423 | + | - [Standalone Services](./services.md) | |||
| 380 | - [Images](./images.md) | 424 | - [Images](./images.md) | ||
M docs/config/server.md +2 -0
| @@ -429,3 +429,5 @@ sudo firewall-cmd --reload | |||
| 429 | 429 | - [Configuration Overview](./index.md) | |
| 430 | 430 | - [Installation Guide](../installation.md) | |
| 431 | 431 | - [Routes Configuration](./routes.md) | |
| 432 | + | - [Standalone Services](./services.md) | |
| 433 | + | - [Traffic Plane Configuration](./traffic.md) | |
| @@ -429,3 +429,5 @@ sudo firewall-cmd --reload | |||||
| 429 | - [Configuration Overview](./index.md) | 429 | - [Configuration Overview](./index.md) | ||
| 430 | - [Installation Guide](../installation.md) | 430 | - [Installation Guide](../installation.md) | ||
| 431 | - [Routes Configuration](./routes.md) | 431 | - [Routes Configuration](./routes.md) | ||
| 432 | + | - [Standalone Services](./services.md) | |||
| 433 | + | - [Traffic Plane Configuration](./traffic.md) | |||
A docs/config/services.md +94 -0
| @@ -0,0 +1,94 @@ | |||
| 1 | + | # Standalone Services | |
| 2 | + | ||
| 3 | + | Standalone services are Gordon-managed containers for non-HTTP workloads such as game servers, databases, or TCP/UDP daemons. Gordon creates, starts, restarts, reconciles, and removes these containers from the `[[services]]` configuration during startup and reload. | |
| 4 | + | ||
| 5 | + | Standalone services are separate from HTTP `[routes]`. Route normal web apps with `[routes]`; use `[[services]]` when Gordon must manage a long-running container that is reached by explicit L4 traffic routers. | |
| 6 | + | ||
| 7 | + | ## Example | |
| 8 | + | ||
| 9 | + | ```toml | |
| 10 | + | [[services]] | |
| 11 | + | name = "rust" | |
| 12 | + | image = "registry.example.com:5000/rust:latest" | |
| 13 | + | enabled = true | |
| 14 | + | env_file = "/srv/gordon/services/rust.env" | |
| 15 | + | ||
| 16 | + | [services.readiness] | |
| 17 | + | type = "log" | |
| 18 | + | path = "/steamcmd/rust/server.log" | |
| 19 | + | contains = "Server startup complete" | |
| 20 | + | timeout = "2m" | |
| 21 | + | ||
| 22 | + | [[services.ports]] | |
| 23 | + | name = "game" | |
| 24 | + | container = 28015 | |
| 25 | + | protocol = "udp" | |
| 26 | + | publish = "127.0.0.1:38015" | |
| 27 | + | ||
| 28 | + | [[services.ports]] | |
| 29 | + | name = "rcon" | |
| 30 | + | container = 28016 | |
| 31 | + | protocol = "tcp" | |
| 32 | + | publish = "127.0.0.1:38016" | |
| 33 | + | trusted_cidrs = ["100.64.0.0/10"] | |
| 34 | + | ``` | |
| 35 | + | ||
| 36 | + | The `publish` address is the host-side loopback address that Gordon's traffic manager dials. Expose it through `[traffic]` routers instead of binding the service container directly to a public interface. | |
| 37 | + | ||
| 38 | + | ## Traffic routing | |
| 39 | + | ||
| 40 | + | Use `service:<service-name>:<port-name>` from TCP, UDP, or TLS passthrough routers: | |
| 41 | + | ||
| 42 | + | ```toml | |
| 43 | + | [entrypoints.rust] | |
| 44 | + | address = "0.0.0.0:28015" | |
| 45 | + | protocol = "udp" | |
| 46 | + | ||
| 47 | + | [[traffic.udp.routers]] | |
| 48 | + | name = "rust-game" | |
| 49 | + | entrypoint = "rust" | |
| 50 | + | service = "service:rust:game" | |
| 51 | + | ||
| 52 | + | [entrypoints.rcon] | |
| 53 | + | address = "0.0.0.0:28016" | |
| 54 | + | protocol = "tcp" | |
| 55 | + | trusted_cidrs = ["100.64.0.0/10"] | |
| 56 | + | ||
| 57 | + | [[traffic.tcp.routers]] | |
| 58 | + | name = "rust-rcon" | |
| 59 | + | entrypoint = "rcon" | |
| 60 | + | service = "service:rust:rcon" | |
| 61 | + | ``` | |
| 62 | + | ||
| 63 | + | Ports named `rcon` default to private. Private ports require non-empty `trusted_cidrs` on both the service port and target entrypoint, and the CIDR sets must match. To intentionally expose an RCON port publicly, set `public = true` on that port. | |
| 64 | + | ||
| 65 | + | ## Volumes | |
| 66 | + | ||
| 67 | + | Explicit volumes are optional: | |
| 68 | + | ||
| 69 | + | ```toml | |
| 70 | + | [[services.volumes]] | |
| 71 | + | source = "rust-data" | |
| 72 | + | target = "/steamcmd/rust" | |
| 73 | + | read_only = false | |
| 74 | + | ``` | |
| 75 | + | ||
| 76 | + | When `[[services.volumes]]` is omitted, Gordon inspects the image `VOLUME` metadata and creates deterministic Gordon-managed named volumes for those paths. If the image has no `VOLUME` metadata, the service is stateless unless the image writes inside its own filesystem. | |
| 77 | + | ||
| 78 | + | Gordon tracks image-discovered managed volumes and only removes those managed volumes when `cleanup.preserve_volumes = false`. Explicit named volumes and bind mounts are not deleted as managed image volumes. | |
| 79 | + | ||
| 80 | + | ## Cleanup | |
| 81 | + | ||
| 82 | + | ```toml | |
| 83 | + | [services.cleanup] | |
| 84 | + | preserve_volumes = true | |
| 85 | + | remove_container = true | |
| 86 | + | ``` | |
| 87 | + | ||
| 88 | + | By default Gordon removes old or disabled service containers while preserving volumes. Set `preserve_volumes = false` only for disposable managed image volumes. | |
| 89 | + | ||
| 90 | + | ## Related | |
| 91 | + | ||
| 92 | + | - [Traffic Plane Configuration](./traffic.md) | |
| 93 | + | - [Configuration Reference](./reference.md) | |
| 94 | + | - [CLI traffic status](../cli/traffic.md) | |
| @@ -0,0 +1,94 @@ | |||||
| 1 | + | # Standalone Services | |||
| 2 | + | ||||
| 3 | + | Standalone services are Gordon-managed containers for non-HTTP workloads such as game servers, databases, or TCP/UDP daemons. Gordon creates, starts, restarts, reconciles, and removes these containers from the `[[services]]` configuration during startup and reload. | |||
| 4 | + | ||||
| 5 | + | Standalone services are separate from HTTP `[routes]`. Route normal web apps with `[routes]`; use `[[services]]` when Gordon must manage a long-running container that is reached by explicit L4 traffic routers. | |||
| 6 | + | ||||
| 7 | + | ## Example | |||
| 8 | + | ||||
| 9 | + | ```toml | |||
| 10 | + | [[services]] | |||
| 11 | + | name = "rust" | |||
| 12 | + | image = "registry.example.com:5000/rust:latest" | |||
| 13 | + | enabled = true | |||
| 14 | + | env_file = "/srv/gordon/services/rust.env" | |||
| 15 | + | ||||
| 16 | + | [services.readiness] | |||
| 17 | + | type = "log" | |||
| 18 | + | path = "/steamcmd/rust/server.log" | |||
| 19 | + | contains = "Server startup complete" | |||
| 20 | + | timeout = "2m" | |||
| 21 | + | ||||
| 22 | + | [[services.ports]] | |||
| 23 | + | name = "game" | |||
| 24 | + | container = 28015 | |||
| 25 | + | protocol = "udp" | |||
| 26 | + | publish = "127.0.0.1:38015" | |||
| 27 | + | ||||
| 28 | + | [[services.ports]] | |||
| 29 | + | name = "rcon" | |||
| 30 | + | container = 28016 | |||
| 31 | + | protocol = "tcp" | |||
| 32 | + | publish = "127.0.0.1:38016" | |||
| 33 | + | trusted_cidrs = ["100.64.0.0/10"] | |||
| 34 | + | ``` | |||
| 35 | + | ||||
| 36 | + | The `publish` address is the host-side loopback address that Gordon's traffic manager dials. Expose it through `[traffic]` routers instead of binding the service container directly to a public interface. | |||
| 37 | + | ||||
| 38 | + | ## Traffic routing | |||
| 39 | + | ||||
| 40 | + | Use `service:<service-name>:<port-name>` from TCP, UDP, or TLS passthrough routers: | |||
| 41 | + | ||||
| 42 | + | ```toml | |||
| 43 | + | [entrypoints.rust] | |||
| 44 | + | address = "0.0.0.0:28015" | |||
| 45 | + | protocol = "udp" | |||
| 46 | + | ||||
| 47 | + | [[traffic.udp.routers]] | |||
| 48 | + | name = "rust-game" | |||
| 49 | + | entrypoint = "rust" | |||
| 50 | + | service = "service:rust:game" | |||
| 51 | + | ||||
| 52 | + | [entrypoints.rcon] | |||
| 53 | + | address = "0.0.0.0:28016" | |||
| 54 | + | protocol = "tcp" | |||
| 55 | + | trusted_cidrs = ["100.64.0.0/10"] | |||
| 56 | + | ||||
| 57 | + | [[traffic.tcp.routers]] | |||
| 58 | + | name = "rust-rcon" | |||
| 59 | + | entrypoint = "rcon" | |||
| 60 | + | service = "service:rust:rcon" | |||
| 61 | + | ``` | |||
| 62 | + | ||||
| 63 | + | Ports named `rcon` default to private. Private ports require non-empty `trusted_cidrs` on both the service port and target entrypoint, and the CIDR sets must match. To intentionally expose an RCON port publicly, set `public = true` on that port. | |||
| 64 | + | ||||
| 65 | + | ## Volumes | |||
| 66 | + | ||||
| 67 | + | Explicit volumes are optional: | |||
| 68 | + | ||||
| 69 | + | ```toml | |||
| 70 | + | [[services.volumes]] | |||
| 71 | + | source = "rust-data" | |||
| 72 | + | target = "/steamcmd/rust" | |||
| 73 | + | read_only = false | |||
| 74 | + | ``` | |||
| 75 | + | ||||
| 76 | + | When `[[services.volumes]]` is omitted, Gordon inspects the image `VOLUME` metadata and creates deterministic Gordon-managed named volumes for those paths. If the image has no `VOLUME` metadata, the service is stateless unless the image writes inside its own filesystem. | |||
| 77 | + | ||||
| 78 | + | Gordon tracks image-discovered managed volumes and only removes those managed volumes when `cleanup.preserve_volumes = false`. Explicit named volumes and bind mounts are not deleted as managed image volumes. | |||
| 79 | + | ||||
| 80 | + | ## Cleanup | |||
| 81 | + | ||||
| 82 | + | ```toml | |||
| 83 | + | [services.cleanup] | |||
| 84 | + | preserve_volumes = true | |||
| 85 | + | remove_container = true | |||
| 86 | + | ``` | |||
| 87 | + | ||||
| 88 | + | By default Gordon removes old or disabled service containers while preserving volumes. Set `preserve_volumes = false` only for disposable managed image volumes. | |||
| 89 | + | ||||
| 90 | + | ## Related | |||
| 91 | + | ||||
| 92 | + | - [Traffic Plane Configuration](./traffic.md) | |||
| 93 | + | - [Configuration Reference](./reference.md) | |||
| 94 | + | - [CLI traffic status](../cli/traffic.md) | |||
M docs/config/traffic.md +29 -3
| @@ -33,9 +33,33 @@ For each accepted connection on a `smart_tcp` entrypoint Gordon: | |||
| 33 | 33 | ||
| 34 | 34 | Malformed HTTP-looking or TLS-looking traffic is rejected instead of bypassing to raw fallback. | |
| 35 | 35 | ||
| 36 | - | ## Network Services | |
| 36 | + | ## Standalone and Network Services | |
| 37 | 37 | ||
| 38 | - | Network services describe non-HTTP backends that L4 routers can target. | |
| 38 | + | Standalone services are Gordon-managed containers that L4 routers can target with `service:<service>:<port-name>`. Define them under `[[services]]` when Gordon should own the container lifecycle. | |
| 39 | + | ||
| 40 | + | ```toml | |
| 41 | + | [[services]] | |
| 42 | + | name = "rust" | |
| 43 | + | image = "registry.example.com:5000/rust:latest" | |
| 44 | + | enabled = true | |
| 45 | + | ||
| 46 | + | [[services.ports]] | |
| 47 | + | name = "game" | |
| 48 | + | container = 28015 | |
| 49 | + | protocol = "udp" | |
| 50 | + | publish = "127.0.0.1:38015" | |
| 51 | + | ||
| 52 | + | [entrypoints.rust] | |
| 53 | + | address = "0.0.0.0:28015" | |
| 54 | + | protocol = "udp" | |
| 55 | + | ||
| 56 | + | [[traffic.udp.routers]] | |
| 57 | + | name = "rust-game" | |
| 58 | + | entrypoint = "rust" | |
| 59 | + | service = "service:rust:game" | |
| 60 | + | ``` | |
| 61 | + | ||
| 62 | + | Network services describe manually managed non-HTTP backends that L4 routers can target. | |
| 39 | 63 | ||
| 40 | 64 | ```toml | |
| 41 | 65 | [[network_services]] | |
| @@ -51,7 +75,8 @@ Service references use: | |||
| 51 | 75 | ||
| 52 | 76 | - `route:<domain>` for configured HTTP routes | |
| 53 | 77 | - `external_route:<domain>` for configured external HTTP routes | |
| 54 | - | - `network_service:<service>:<port-name>` for TCP, UDP, and TLS passthrough | |
| 78 | + | - `network_service:<service>:<port-name>` for manually managed TCP, UDP, and TLS passthrough backends | |
| 79 | + | - `service:<service>:<port-name>` for Gordon-managed standalone service backends | |
| 55 | 80 | ||
| 56 | 81 | `static:<name>` is reserved and currently unsupported. | |
| 57 | 82 | ||
| @@ -152,4 +177,5 @@ UDP sessions are keyed by client address and expire after `idle_timeout`. If `ma | |||
| 152 | 177 | ||
| 153 | 178 | - [Server Settings](./server.md) | |
| 154 | 179 | - [Routes](./routes.md) | |
| 180 | + | - [Standalone Services](./services.md) | |
| 155 | 181 | - [CLI traffic status](../cli/traffic.md) | |
| @@ -33,9 +33,33 @@ For each accepted connection on a `smart_tcp` entrypoint Gordon: | |||||
| 33 | 33 | ||||
| 34 | Malformed HTTP-looking or TLS-looking traffic is rejected instead of bypassing to raw fallback. | 34 | Malformed HTTP-looking or TLS-looking traffic is rejected instead of bypassing to raw fallback. | ||
| 35 | 35 | ||||
| 36 | - | ## Network Services | 36 | + | ## Standalone and Network Services |
| 37 | 37 | ||||
| 38 | - | Network services describe non-HTTP backends that L4 routers can target. | 38 | + | Standalone services are Gordon-managed containers that L4 routers can target with `service:<service>:<port-name>`. Define them under `[[services]]` when Gordon should own the container lifecycle. |
| 39 | + | ||||
| 40 | + | ```toml | |||
| 41 | + | [[services]] | |||
| 42 | + | name = "rust" | |||
| 43 | + | image = "registry.example.com:5000/rust:latest" | |||
| 44 | + | enabled = true | |||
| 45 | + | ||||
| 46 | + | [[services.ports]] | |||
| 47 | + | name = "game" | |||
| 48 | + | container = 28015 | |||
| 49 | + | protocol = "udp" | |||
| 50 | + | publish = "127.0.0.1:38015" | |||
| 51 | + | ||||
| 52 | + | [entrypoints.rust] | |||
| 53 | + | address = "0.0.0.0:28015" | |||
| 54 | + | protocol = "udp" | |||
| 55 | + | ||||
| 56 | + | [[traffic.udp.routers]] | |||
| 57 | + | name = "rust-game" | |||
| 58 | + | entrypoint = "rust" | |||
| 59 | + | service = "service:rust:game" | |||
| 60 | + | ``` | |||
| 61 | + | ||||
| 62 | + | Network services describe manually managed non-HTTP backends that L4 routers can target. | |||
| 39 | 63 | ||||
| 40 | ```toml | 64 | ```toml | ||
| 41 | [[network_services]] | 65 | [[network_services]] | ||
| @@ -51,7 +75,8 @@ Service references use: | |||||
| 51 | 75 | ||||
| 52 | - `route:<domain>` for configured HTTP routes | 76 | - `route:<domain>` for configured HTTP routes | ||
| 53 | - `external_route:<domain>` for configured external HTTP routes | 77 | - `external_route:<domain>` for configured external HTTP routes | ||
| 54 | - | - `network_service:<service>:<port-name>` for TCP, UDP, and TLS passthrough | 78 | + | - `network_service:<service>:<port-name>` for manually managed TCP, UDP, and TLS passthrough backends |
| 79 | + | - `service:<service>:<port-name>` for Gordon-managed standalone service backends | |||
| 55 | 80 | ||||
| 56 | `static:<name>` is reserved and currently unsupported. | 81 | `static:<name>` is reserved and currently unsupported. | ||
| 57 | 82 | ||||
| @@ -152,4 +177,5 @@ UDP sessions are keyed by client address and expire after `idle_timeout`. If `ma | |||||
| 152 | 177 | ||||
| 153 | - [Server Settings](./server.md) | 178 | - [Server Settings](./server.md) | ||
| 154 | - [Routes](./routes.md) | 179 | - [Routes](./routes.md) | ||
| 180 | + | - [Standalone Services](./services.md) | |||
| 155 | - [CLI traffic status](../cli/traffic.md) | 181 | - [CLI traffic status](../cli/traffic.md) | ||
M gordon.toml.example +40 -0
| @@ -174,6 +174,46 @@ keep = 14 | |||
| 174 | 174 | [external_routes] | |
| 175 | 175 | # "status.example.com" = "127.0.0.1:9000" | |
| 176 | 176 | ||
| 177 | + | # Gordon-managed standalone L4 services. Use service:<name>:<port> from | |
| 178 | + | # traffic routers to expose published loopback ports. | |
| 179 | + | # [[services]] | |
| 180 | + | # name = "rust" | |
| 181 | + | # image = "registry.example.com:5000/rust:latest" | |
| 182 | + | # enabled = true | |
| 183 | + | # env_file = "/srv/gordon/services/rust.env" | |
| 184 | + | # | |
| 185 | + | # [[services.ports]] | |
| 186 | + | # name = "game" | |
| 187 | + | # container = 28015 | |
| 188 | + | # protocol = "udp" | |
| 189 | + | # publish = "127.0.0.1:38015" | |
| 190 | + | # | |
| 191 | + | # [[services.ports]] | |
| 192 | + | # name = "rcon" | |
| 193 | + | # container = 28016 | |
| 194 | + | # protocol = "tcp" | |
| 195 | + | # publish = "127.0.0.1:38016" | |
| 196 | + | # trusted_cidrs = ["100.64.0.0/10"] | |
| 197 | + | # | |
| 198 | + | # [entrypoints.rust] | |
| 199 | + | # address = "0.0.0.0:28015" | |
| 200 | + | # protocol = "udp" | |
| 201 | + | # | |
| 202 | + | # [[traffic.udp.routers]] | |
| 203 | + | # name = "rust-game" | |
| 204 | + | # entrypoint = "rust" | |
| 205 | + | # service = "service:rust:game" | |
| 206 | + | # | |
| 207 | + | # [entrypoints.rcon] | |
| 208 | + | # address = "0.0.0.0:28016" | |
| 209 | + | # protocol = "tcp" | |
| 210 | + | # trusted_cidrs = ["100.64.0.0/10"] | |
| 211 | + | # | |
| 212 | + | # [[traffic.tcp.routers]] | |
| 213 | + | # name = "rust-rcon" | |
| 214 | + | # entrypoint = "rcon" | |
| 215 | + | # service = "service:rust:rcon" | |
| 216 | + | ||
| 177 | 217 | [network_groups] | |
| 178 | 218 | # "backend" = ["app.example.com", "api.example.com"] | |
| 179 | 219 | ||
| @@ -174,6 +174,46 @@ keep = 14 | |||||
| 174 | [external_routes] | 174 | [external_routes] | ||
| 175 | # "status.example.com" = "127.0.0.1:9000" | 175 | # "status.example.com" = "127.0.0.1:9000" | ||
| 176 | 176 | ||||
| 177 | + | # Gordon-managed standalone L4 services. Use service:<name>:<port> from | |||
| 178 | + | # traffic routers to expose published loopback ports. | |||
| 179 | + | # [[services]] | |||
| 180 | + | # name = "rust" | |||
| 181 | + | # image = "registry.example.com:5000/rust:latest" | |||
| 182 | + | # enabled = true | |||
| 183 | + | # env_file = "/srv/gordon/services/rust.env" | |||
| 184 | + | # | |||
| 185 | + | # [[services.ports]] | |||
| 186 | + | # name = "game" | |||
| 187 | + | # container = 28015 | |||
| 188 | + | # protocol = "udp" | |||
| 189 | + | # publish = "127.0.0.1:38015" | |||
| 190 | + | # | |||
| 191 | + | # [[services.ports]] | |||
| 192 | + | # name = "rcon" | |||
| 193 | + | # container = 28016 | |||
| 194 | + | # protocol = "tcp" | |||
| 195 | + | # publish = "127.0.0.1:38016" | |||
| 196 | + | # trusted_cidrs = ["100.64.0.0/10"] | |||
| 197 | + | # | |||
| 198 | + | # [entrypoints.rust] | |||
| 199 | + | # address = "0.0.0.0:28015" | |||
| 200 | + | # protocol = "udp" | |||
| 201 | + | # | |||
| 202 | + | # [[traffic.udp.routers]] | |||
| 203 | + | # name = "rust-game" | |||
| 204 | + | # entrypoint = "rust" | |||
| 205 | + | # service = "service:rust:game" | |||
| 206 | + | # | |||
| 207 | + | # [entrypoints.rcon] | |||
| 208 | + | # address = "0.0.0.0:28016" | |||
| 209 | + | # protocol = "tcp" | |||
| 210 | + | # trusted_cidrs = ["100.64.0.0/10"] | |||
| 211 | + | # | |||
| 212 | + | # [[traffic.tcp.routers]] | |||
| 213 | + | # name = "rust-rcon" | |||
| 214 | + | # entrypoint = "rcon" | |||
| 215 | + | # service = "service:rust:rcon" | |||
| 216 | + | ||||
| 177 | [network_groups] | 217 | [network_groups] | ||
| 178 | # "backend" = ["app.example.com", "api.example.com"] | 218 | # "backend" = ["app.example.com", "api.example.com"] | ||
| 179 | 219 | ||||