https://github.com/leostera/gumgum
https://github.com/leostera/gumgum
Last synced: about 1 month ago
JSON representation
- Host: GitHub
- URL: https://github.com/leostera/gumgum
- Owner: leostera
- Created: 2026-05-22T13:44:12.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-22T22:36:44.000Z (about 2 months ago)
- Last Synced: 2026-05-22T22:56:48.478Z (about 2 months ago)
- Language: Rust
- Size: 6.47 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# gumgum
gumgum is a self-hosted app platform for your VPS or local machine. It gives small projects a Cloudflare-like workflow without handing the runtime to a hosted control plane: set up one server, deploy workers, attach databases/KV/buckets/queues/secrets, inspect logs and events, and publish routes from one CLI.
License: GNU Affero General Public License v3.0 only (`AGPL-3.0-only`).
## Table of contents
- [Why gumgum?](#why-gumgum)
- [Core concepts](#core-concepts)
- [What gumgum manages](#what-gumgum-manages)
- [Install the CLI](#install-the-cli)
- [Set up a server](#set-up-a-server)
- [Local server](#local-server)
- [Remote server](#remote-server)
- [Inspect and remove servers](#inspect-and-remove-servers)
- [Create a project or workspace](#create-a-project-or-workspace)
- [Create a workspace](#create-a-workspace)
- [Create workers](#create-workers)
- [Manage domains and ingress](#manage-domains-and-ingress)
- [Manage resources](#manage-resources)
- [Databases](#databases)
- [KV namespaces](#kv-namespaces)
- [Buckets](#buckets)
- [Queues](#queues)
- [Secrets](#secrets)
- [Bind resources to workers](#bind-resources-to-workers)
- [Observability and platform services](#observability-and-platform-services)
- [Deploy](#deploy)
- [Logs, environment, status, and events](#logs-environment-status-and-events)
- [Graph inspection](#graph-inspection)
- [Bucket object operations](#bucket-object-operations)
- [Publish](#publish)
- [Rollback and revisions](#rollback-and-revisions)
- [Configuration](#configuration)
- [JSON output](#json-output)
- [End-to-end example](#end-to-end-example)
- [Command reference](#command-reference)
- [License](#license)
## Why gumgum?
- **One server, one simple platform.** Turn a Linux host or your local computer into a small PaaS.
- **Batteries included.** gumgum starts the backing services most apps need: Postgres, Redis, MinIO/S3 buckets, Redpanda queues, secrets, and an observability stack.
- **Project-first workflow.** Create resources, bind them to workers, deploy, view logs, and inspect events from the project directory.
- **Local-first control.** The daemon runs on your server. Your CLI sends intent; gumgum handles runtime/provider convergence there.
- **Human CLI by default.** Commands print readable output unless you ask for `--json`.
## Core concepts
- **Server**: the machine that runs `gumgumd`, Docker workloads, the gateway, and built-in providers. A server can have a public control-plane domain used for default routes.
- **Domain**: a DNS zone or delegated domain GumGum may use for routes. Domains can be manual or Cloudflare-managed.
- **Project**: one app/workspace with a production domain in `gumgum.toml`.
- **Environment**: an isolated deployment target. GumGum uses `preview` and `prod`; app/provider containers and data objects are environment-scoped.
- **Workspace**: a directory containing multiple worker projects.
- **Resource**: a backing service object such as a database, KV namespace, bucket, queue, or secret.
- **Binding**: an environment projection from a resource into a worker, for example `DATABASE_URL` or `UPLOADS_BUCKET`.
- **Desired state**: the resources, bindings, routes, and deployments gumgum should maintain on the server.
## What gumgum manages
gumgum can manage:
- app/worker deployment to Docker
- explicit HTTP routes through the gumgum gateway
- Postgres databases on host volumes under `/gumgum/volumes/providers/*/postgres`
- Redis KV namespaces on host volumes under `/gumgum/volumes/providers/*/redis` with append-only persistence
- MinIO/S3 buckets and bucket objects on host volumes under `/gumgum/volumes/providers/*/minio`
- Redpanda queues/topics on host volumes under `/gumgum/volumes/providers/*/redpanda`
- platform secrets and observability services
- Prometheus scraping, host metrics, Docker/container metrics, Grafana dashboards, Loki logs, Tempo traces, and OTEL collector plumbing
- worker environment bindings
- logs, events, grouped event summaries, rollback metadata, and publish previews
## Install the CLI
Build the CLI from this repository:
```bash
git clone https://github.com/leostera/gumgum.git
cd gumgum
cargo build --release
install -m 0755 target/release/gumgum ~/.local/bin/gumgum
```
Make sure `~/.local/bin` is on your `PATH`:
```bash
export PATH="$HOME/.local/bin:$PATH"
```
Check the CLI:
```bash
gumgum --help
gumgum version
```
## Set up a server
`gumgum server add` is the main setup command. It installs/configures `gumgumd`, starts built-in providers, and saves the server locally. GumGum upgrades also migrate the daemon's own internal SQLite graph database automatically before the daemon starts serving requests.
### Local server
Use this when you want to run apps on your own machine:
```bash
gumgum server add 0.0.0.0 --name local --domain example.dev
```
This configures a local gumgum server named `local`. GumGum does not install local DNS resolver entries; use real DNS, `/etc/hosts`, or `curl --resolve` for local testing.
### Remote server
Use this when you want to run apps on another machine over SSH:
```bash
gumgum server add 203.0.113.10 --name prod --domain example.com
```
If SSH needs a user:
```bash
gumgum server add 203.0.113.10 \
--user root \
--name prod \
--domain example.com
```
Preview setup without changing anything:
```bash
gumgum --dry-run server add 203.0.113.10 --name prod --domain example.com
```
### Inspect and remove servers
List configured servers:
```bash
gumgum server list
```
Ping the daemon:
```bash
gumgum server ping --host prod
```
Inspect daemon capabilities:
```bash
gumgum server capabilities list --host prod
```
Require specific daemon capabilities:
```bash
gumgum server capabilities list --host prod \
--require gumgum:events,gumgum:buckets:objects
```
Remove a server from your local config:
```bash
gumgum server rm prod
```
Removing a server record does not delete remote containers or resources.
## Create a project or workspace
### Create a workspace
`gumgum init` creates a workspace manifest in the current directory:
```bash
gumgum init --name my-app
```
Useful options:
```bash
gumgum init --name my-app --domain example.com
gumgum init --name my-app --namespace production
gumgum init --name my-app --force
```
### Create workers
Workers are managed with `gumgum worker`, separate from workspace initialization.
Create workers inside the current workspace:
```bash
gumgum worker create api --port 3000
gumgum worker create jobs --port 3001
```
This creates worker folders with their own `gumgum.toml` files and adds them to the workspace members list.
Choose an explicit directory or zones:
```bash
gumgum worker create api --dir apps/api --port 3000
gumgum worker create api --zone example.com --zone example.net
```
List workers:
```bash
gumgum worker list
```
Remove a worker from the workspace:
```bash
gumgum worker delete api
```
`worker delete` only removes the worker from the workspace manifest. It never deletes source files.
Workspace-aware commands such as `logs`, `env`, and `publish --dry-run` can operate across all workers from the workspace root.
## Manage domains and ingress
Servers run the GumGum control plane; domains describe DNS zones that a server can use for routes. Add a domain after a server is available:
```bash
gumgum domain add leostera.dev --server starbase2 --provider cloudflare --ingress cloudflare
```
`--provider manual` records the domain and prints the desired intent without changing DNS. `--provider cloudflare` prompts for a Cloudflare API token and saves it on the GumGum server so future route convergence can manage DNS/tunnel state there.
Required Cloudflare token permissions:
| Scope | Permission | Applies to |
| --- | --- | --- |
| Zone | DNS: Edit | Every domain GumGum should manage |
| Zone | Zone: Read | Every domain GumGum should manage |
| Account | Cloudflare Tunnel: Edit | Account used for Cloudflare Tunnel ingress |
If `Cloudflare Tunnel: Edit` is not available in your dashboard, use `Account / Zero Trust: Edit` as the broader fallback. You do not need Zone Access permissions unless you are separately creating Cloudflare Access apps/policies. Scoping the token to all zones is convenient when one GumGum server will host multiple domains; for stricter isolation, scope it only to domains GumGum should manage and update/recreate it before adding another domain.
As setup sugar, `gumgum server add ... --domain leostera.dev --ingress cloudflare` installs the server and then adds that domain with Cloudflare ingress.
When a deployed worker has `public = true`, GumGum converges public ingress for its derived route. For a Cloudflare-managed domain with Cloudflare ingress, deploy ensures a proxied CNAME to the GumGum Cloudflare Tunnel and adds a tunnel rule to the server gateway.
## Manage resources
Resource commands share a common shape:
```bash
gumgum list
gumgum create
gumgum delete --preview
gumgum delete
```
`` is one of:
- `db`
- `kv`
- `bucket`
- `queue`
- `secret`
Pass `--host ` when you want to target a specific server instead of the configured/default one.
### Databases
Create and list Postgres databases:
```bash
gumgum db create app-db
gumgum db list
```
Delete safely with a preview first:
```bash
gumgum db delete app-db --preview
gumgum db delete app-db
```
Create with an explicit namespace or root domain when needed:
```bash
gumgum db create app-db --namespace prod
gumgum db create app-db --domain example.com
```
### KV namespaces
Create and list Redis-backed KV namespaces:
```bash
gumgum kv create app-cache
gumgum kv list
```
Delete:
```bash
gumgum kv delete app-cache --preview
gumgum kv delete app-cache
```
### Buckets
Create and list MinIO/S3-backed buckets:
```bash
gumgum bucket create uploads
gumgum bucket list
```
Delete:
```bash
gumgum bucket delete uploads --preview
gumgum bucket delete uploads
```
Bucket object commands are covered in [Bucket object operations](#bucket-object-operations).
### Queues
Create and list Redpanda-backed queues/topics:
```bash
gumgum queue create jobs
gumgum queue list
```
Delete:
```bash
gumgum queue delete jobs --preview
gumgum queue delete jobs
```
### Secrets
Create and list secrets:
```bash
gumgum secret create api-token
gumgum secret list
```
Delete:
```bash
gumgum secret delete api-token --preview
gumgum secret delete api-token
```
## Bind resources to workers
Bindings project resource connection information into a worker's environment.
General shape:
```bash
gumgum bind --to --as
gumgum unbind --to --as --preview
gumgum unbind --to --as
```
Examples:
```bash
gumgum db bind app-db --to api --as DATABASE_URL
gumgum kv bind app-cache --to api --as CACHE
gumgum bucket bind uploads --to api --as UPLOADS_BUCKET
gumgum queue bind jobs --to api --as JOBS_QUEUE
gumgum secret bind api-token --to api --as API_TOKEN
```
Bindings default to read-write access. You can request another access mode:
```bash
gumgum bucket bind uploads --to api --as UPLOADS_BUCKET --access read-only
```
## Observability and platform services
GumGum boots a singleton platform stack on each server, separate from per-environment app data providers:
| Service | Container | Purpose |
| --- | --- | --- |
| Vaultwarden | `gumgum-vaultwarden` | platform-scoped secret service |
| OpenTelemetry Collector | `gumgum-otel` | OTLP ingress and forwarding to Tempo |
| Prometheus | `gumgum-prometheus` | app/platform/host/container metrics |
| Grafana | `gumgum-grafana` | dashboards, logs, traces, and datasources at `grafana.` |
| Loki | `gumgum-loki` | log backend |
| Tempo | `gumgum-tempo` | trace backend |
| Grafana Alloy | `gumgum-alloy` | Docker log shipping to Loki |
| node-exporter | `gumgum-node-exporter` | host metrics |
| cAdvisor | `gumgum-cadvisor` | Docker/container metrics |
| Docker socket proxy | `gumgum-docker-proxy` | read-only Docker discovery for Prometheus |
| Caddy | `gumgum-caddy` | TLS ingress, route labels, and ingress trace spans |
Declare app metrics, required secrets, Grafana datasource files, and dashboard files in a worker manifest:
```toml
[observability]
enable = true
prometheus_metrics = "/_/metrics"
[observability.grafana]
sources = "../grafana/sources.json"
[[secret]]
name = "my-app/api-token"
binding = "API_TOKEN"
[[dashboard]]
name = "API Overview"
path = "../grafana/dashboards/api-overview.json"
```
When deployed, GumGum:
- injects declared secret bindings and OTEL environment into workers
- configures Prometheus scrapes for declared metrics endpoints
- labels app containers for Docker service discovery (`prometheus.scrape=true`, `prometheus.port`, `prometheus.path`, `prometheus.label_*`)
- ships GumGum-managed Docker logs to Loki with `project`, `domain`, `environment`, `worker`, and platform labels
- enables Tempo traces from the Caddy ingress span through instrumented workers and logical provider operations
- applies Grafana datasources and dashboards through the daemon
- stores Grafana dashboards under `` → `` folders
Grafana dashboards should include a top-level `GUMGUM_ENV` variable for switching between `preview` and `prod`. Query app metrics with that environment label, for example:
```promql
visit_counter_requests_total{environment="$GUMGUM_ENV"}
```
`gumgum status` includes platform provider health. A healthy server with the current default stack reports `Providers: 10/10 running`.
Stateful platform services use host volume paths under `/gumgum/volumes/platform/*` for durability across container and machine restarts: Grafana, Prometheus, Loki, Tempo, Alloy, Vaultwarden, and Caddy all keep their data outside the container filesystem.
## Deploy
Deploy the current project:
```bash
gumgum deploy
```
Deploy a specific manifest:
```bash
gumgum deploy api/gumgum.toml
```
Target a specific server:
```bash
gumgum deploy --host prod
```
Deploy the prod environment:
```bash
gumgum deploy --env prod
```
Deployments are rolling by default. GumGum starts a new revision-specific container, waits for its health check to pass, lets Caddy discover the new route labels, and then removes stale containers for that worker/route. Running containers therefore use stable GumGum labels but revision-suffixed Docker names, for example:
```text
gumgum-prod-dev-leostera-visit-counter-api-d6fcec95161ecc48
```
Delete desired deployment state for a worker:
```bash
gumgum deploy api/gumgum.toml --delete
```
Preview deploy behavior without mutating state:
```bash
gumgum --dry-run deploy
```
Ingress routes are declared in worker manifests:
```toml
[[ingress]]
name = "http"
protocol = "http"
port = 3000
public = true
```
Default deploy routes use the server/control-plane domain:
```text
..
# api.visit-counter.leostera.dev
```
`deploy --env prod` uses the project domain from the workspace manifest:
```toml
[project]
name = "visit-counter"
domain = "kava.fund"
server = "starbase2"
```
Ingress records are relative to that route base:
| `record` | Preview env route | Prod env route |
| --- | --- | --- |
| omitted | `..` | `.` |
| `"api"` | `api..` | `api.` |
| `"@"` | `.` | `` |
For example, to publish the project apex instead of `api.`:
```toml
[[ingress]]
name = "http"
protocol = "http"
port = 3000
public = true
record = "@"
```
## Logs, environment, status, and events
Check overall server status:
```bash
gumgum status
gumgum status --host prod
```
Show logs for the current project or workspace:
```bash
gumgum logs
gumgum logs --tail 200
```
The CLI can still read Docker logs directly, and the platform also ships GumGum-managed Docker logs into Loki. In Grafana Explore, useful Loki selectors include:
```logql
{gumgum_managed="deployment", project="visit-counter"}
{environment="prod", worker="api-prod"}
```
Show logs for one worker:
```bash
gumgum logs api
gumgum logs api --tail 100
```
Follow logs for a single worker:
```bash
gumgum logs api --follow
```
Print environment for the current project or workspace:
```bash
gumgum env
```
Narrow environment output:
```bash
gumgum env --project my-app
gumgum env --worker api
gumgum env --project my-app --worker api
```
Environment keys are namespaced by worker by default:
```dotenv
API_DATABASE_URL=...
API_CACHE_URL=...
```
Use `--qualified` when exporting a whole workspace and you want project + worker prefixes:
```bash
gumgum env --qualified
```
```dotenv
MY_APP_API_DATABASE_URL=...
MY_APP_API_CACHE_URL=...
```
Trace data is sent to Tempo through the OpenTelemetry Collector. Instrumented apps can use standard OTEL environment variables projected by GumGum; Caddy emits the ingress span and propagates W3C trace context upstream. In Grafana Traces, a visit-counter request should show a chain like:
```text
gumgum-caddy api-prod-ingress
api-prod GET /
api-prod redis increment user counter
api-prod minio put visit request
api-prod redpanda produce visit event
visit-counter-worker-prod visit-counter.process_message
visit-counter-worker-prod minio get visit request
visit-counter-worker-prod postgres insert visit
```
Show control-plane events:
```bash
gumgum events
gumgum events --limit 20
gumgum events --grouped
gumgum events --kind mutation
gumgum events --kind reconciliation
```
## Graph inspection
Render the desired graph:
```bash
gumgum graph
gumgum graph show
```
Show the subgraph affected by a target:
```bash
gumgum graph affected worker/api
gumgum graph affected api
```
The target must exist in the workspace/desired graph. Invalid targets fail instead of returning an empty affected list.
## Bucket object operations
Bucket object commands only exist under `gumgum bucket`.
List objects in a bucket:
```bash
gumgum bucket ls uploads
gumgum bucket ls uploads raw/
```
Read an object to stdout:
```bash
gumgum bucket get uploads path/to/file.json
```
Copy local to remote:
```bash
gumgum bucket cp ./local.json uploads/local.json
```
Copy remote to local:
```bash
gumgum bucket cp uploads/local.json ./local-copy.json
```
Copy remote to remote:
```bash
gumgum bucket cp uploads/local.json uploads/archive/local.json
```
Sync a remote prefix to another remote prefix:
```bash
gumgum bucket sync uploads/raw uploads/archive/raw
```
Remove an object:
```bash
gumgum bucket rm uploads archive/local.json
```
## Publish
Preview public publishing before changing public route state:
```bash
gumgum --dry-run publish
```
Preview a specific target:
```bash
gumgum --dry-run publish api/gumgum.toml
```
Plan an explicit public domain:
```bash
gumgum --dry-run publish api/gumgum.toml --public-domain api.example.com
```
Apply publishing only after reviewing the dry-run:
```bash
gumgum publish api/gumgum.toml --public-domain api.example.com
```
Workspace dry-runs plan each member. `--public-domain` is only valid when publishing one worker.
## Rollback and revisions
Preview rollback for the current project:
```bash
gumgum rollback --preview
```
Preview rollback for a worker:
```bash
gumgum rollback api/gumgum.toml --worker api --preview
```
List deployment revisions:
```bash
gumgum rollback api/gumgum.toml --worker api --revisions
gumgum rollback api/gumgum.toml --worker api --revisions --limit 20
```
Preview or apply a specific revision:
```bash
gumgum rollback api/gumgum.toml --worker api --revision-id 12 --preview
gumgum rollback api/gumgum.toml --worker api --revision-id 12
```
Delete stale revision metadata without changing containers:
```bash
gumgum rollback api/gumgum.toml --worker api --delete-revision-id 12
```
## Configuration
View local gumgum config:
```bash
gumgum config list
```
Read and write known config keys:
```bash
gumgum config get ui.color
gumgum config set ui.color true
gumgum config get format
gumgum config set format human
gumgum config get registry_port
gumgum config set registry_port 5000
```
Config is schema-backed, not arbitrary key/value storage. Unknown keys fail with a human-readable error.
Server-scoped config uses the same schema:
```bash
gumgum server config --host prod list
gumgum server config --host prod get ui.color
gumgum server config --host prod set ui.color true
```
## JSON output
Use `--json` when scripting:
```bash
gumgum --json status
gumgum --json events --limit 10
gumgum --json db list
gumgum --json bucket list
```
Errors are human-readable by default and structured as JSON only with `--json`.
## End-to-end example
The `examples/visit-counter` workspace demonstrates:
- API and background worker projects
- preview/prod environment isolation
- DB/KV/bucket/queue resources
- platform-scoped secrets and observability declarations
- Prometheus metrics from both API and worker
- Loki logs labelled by project/domain/environment/worker
- Tempo traces spanning Caddy → API → Redis/MinIO/Redpanda logical operations → worker → MinIO/Postgres logical operations
- Grafana datasource/dashboard application under `` → `` folders
- resource bindings
- rolling deploys with revision-suffixed containers
- logs and environment inspection
- events and grouped events
- rollback previews
- publish dry-runs
Try it after configuring a server:
```bash
cd examples/visit-counter
gumgum db create visits
gumgum kv create user-counters
gumgum bucket create visit-requests
gumgum queue create visit-events
gumgum db bind visits --to worker --as DATABASE_URL
gumgum kv bind user-counters --to api --as USER_COUNTERS
gumgum bucket bind visit-requests --to api --as VISIT_REQUESTS_BUCKET
gumgum bucket bind visit-requests --to worker --as VISIT_REQUESTS_BUCKET
gumgum queue bind visit-events --to api --as VISIT_EVENTS_QUEUE
gumgum queue bind visit-events --to worker --as VISIT_EVENTS_QUEUE
gumgum deploy --env preview
gumgum deploy --env prod
gumgum logs
gumgum events --grouped
gumgum --dry-run publish
```
The API worker uses `record = "@"`, so prod serves the project domain itself, for example `https://kava.fund/`, while preview serves the project route under the server domain, for example `https://visit-counter.leostera.dev/`.
## Command reference
Global flags:
```bash
gumgum --json
gumgum --dry-run
```
Server commands:
```bash
gumgum server
gumgum server list
gumgum server add [--name ] [--user ] [--domain ] [--ingress direct|cloudflare]
gumgum server rm
gumgum server ping --host
gumgum server capabilities list --host [--require ]
gumgum server config --host list|get|set
gumgum server upgrade --host
```
Domain commands:
```bash
gumgum domain add [--server ] [--provider manual|cloudflare] [--ingress direct|cloudflare]
```
Project/runtime commands:
```bash
gumgum init [--name ] [--domain ] [--server ] [--force]
gumgum worker create [--port ] [--dir ] [--force]
gumgum worker list [workspace]
gumgum worker delete [workspace]
gumgum deploy [path] [--host ] [--env preview|prod] [--delete]
gumgum publish [target] [--host ] [--public-domain ] [--tunnel ]
gumgum logs [path-or-worker] [--host ] [--tail ] [--follow]
gumgum env [path] [--host ] [--project ] [--worker ] [--qualified]
gumgum status [--host ]
gumgum events [--host ] [--limit ] [--kind mutation|reconciliation] [--grouped]
gumgum graph [--host ] [show|affected ]
gumgum rollback [path] [--host ] [--worker ] [--preview] [--revisions] [--revision-id ] [--delete-revision-id ]
```
Resource commands:
```bash
gumgum db list|create|delete|bind|unbind
gumgum kv list|create|delete|bind|unbind
gumgum bucket list|create|delete|bind|unbind|ls|get|rm|cp|sync
gumgum queue list|create|delete|bind|unbind
gumgum secret list|create|delete|bind|unbind
```
## License
GumGum is licensed under the GNU Affero General Public License v3.0 only (`AGPL-3.0-only`). See [`LICENSE`](LICENSE) for the full license text.