From 902735bddf813546dbbf43cf1c3e760775a8975c Mon Sep 17 00:00:00 2001 From: "Jakub A. W" Date: Sun, 24 May 2026 21:04:12 -0700 Subject: [PATCH 1/3] docs(getting-started): add solid start guide --- docs/docs.json | 3 +- docs/getting-started/quickstart.mdx | 1 + docs/getting-started/solid-start.mdx | 232 +++++++++++++++++++++++++++ 3 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 docs/getting-started/solid-start.mdx diff --git a/docs/docs.json b/docs/docs.json index a32d09b6..ddf7664d 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -38,7 +38,8 @@ "group": "Getting Started", "icon": "rocket", "pages": [ - "getting-started/quickstart" + "getting-started/quickstart", + "getting-started/solid-start" ] }, { diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx index 32bb1488..1b47dc31 100644 --- a/docs/getting-started/quickstart.mdx +++ b/docs/getting-started/quickstart.mdx @@ -120,6 +120,7 @@ Use one of those model IDs in your requests. ## Next Steps +- Prepare a production deployment: [Solid Start](/getting-started/solid-start) - Understand response caching: [Cache](/features/cache) - Add spend limits: [Budgets](/features/budgets) - Configure production settings: [Configuration](/advanced/configuration) diff --git a/docs/getting-started/solid-start.mdx b/docs/getting-started/solid-start.mdx new file mode 100644 index 00000000..b197c228 --- /dev/null +++ b/docs/getting-started/solid-start.mdx @@ -0,0 +1,232 @@ +--- +title: "Solid Start" +description: "Run GoModel in a production-ready way with a .env file, Docker, Docker Compose, or a native binary." +icon: "shield-check" +--- + +This page shows a practical production baseline for GoModel. It assumes you +will run GoModel behind HTTPS, keep secrets out of shell history, persist +storage across restarts, and protect the gateway with a master key. + +## Production baseline + +Start with these decisions: + +| Area | Recommended default | +| ---- | ------------------- | +| Secrets | Put credentials in a `.env` file or your orchestrator's secret manager | +| Authentication | Set `GOMODEL_MASTER_KEY` before exposing the gateway | +| Logging | Use `LOG_FORMAT=json`; enable audit logging only with a retention policy | +| Storage | Use SQLite for one instance, PostgreSQL or MongoDB for multiple instances | +| Network | Terminate TLS at a reverse proxy or load balancer, then forward to GoModel | +| Health checks | Use public `GET /health` from your proxy, load balancer, or process supervisor | + + + Do not expose GoModel to the internet without `GOMODEL_MASTER_KEY`. When the + key is empty, API routes are intentionally unprotected for local development. + + +## Create a production .env file + +Create `/opt/gomodel/.env` or another path managed by your deployment system: + +```bash +PORT=8080 +LOG_FORMAT=json +LOG_LEVEL=info +BODY_SIZE_LIMIT=10M +GOMODEL_MASTER_KEY=replace-with-a-long-random-secret + +# Provider credentials. Set only the providers you use. +OPENAI_API_KEY=sk-... +# ANTHROPIC_API_KEY=sk-ant-... +# GEMINI_API_KEY=... +# OPENROUTER_API_KEY=sk-or-... + +# Storage: SQLite is fine for a single GoModel instance. +STORAGE_TYPE=sqlite +SQLITE_PATH=/app/data/gomodel.db + +# Audit logs are optional. Keep bodies off unless you explicitly need them. +LOGGING_ENABLED=true +LOGGING_LOG_BODIES=false +LOGGING_LOG_HEADERS=false +LOGGING_RETENTION_DAYS=30 + +# Usage tracking is enabled by default; keep a retention window. +USAGE_ENABLED=true +USAGE_RETENTION_DAYS=90 + +# Keep admin endpoints and dashboard enabled only when they are protected by auth +# and reachable through your intended network path. +ADMIN_ENDPOINTS_ENABLED=true +ADMIN_UI_ENABLED=true +``` + +Protect the file on the host: + +```bash +sudo chown root:root /opt/gomodel/.env +sudo chmod 600 /opt/gomodel/.env +``` + + + SQLite uses sidecar files such as `gomodel.db-wal` and `gomodel.db-shm`, so + mount a directory, not only the `.db` file. + + +## Run with Docker + +Use Docker when you want the smallest operational surface. Mount durable +storage and load secrets from the `.env` file: + +```bash +sudo mkdir -p /opt/gomodel/data +sudo chown 65532:65532 /opt/gomodel/data + +docker run -d --name gomodel \ + --restart unless-stopped \ + --env-file /opt/gomodel/.env \ + -p 127.0.0.1:8080:8080 \ + -v /opt/gomodel/data:/app/data \ + enterpilot/gomodel +``` + +Bind to `127.0.0.1` when a reverse proxy on the same host terminates HTTPS. If +GoModel sits behind a cloud load balancer or another container network, publish +the port according to that network boundary instead. + +## Run with Docker Compose + +Use Compose when you also run Redis, PostgreSQL, MongoDB, Prometheus, or a +reverse proxy on the same host. + +```yaml +services: + gomodel: + image: enterpilot/gomodel + restart: unless-stopped + env_file: + - /opt/gomodel/.env + ports: + - "127.0.0.1:8080:8080" + volumes: + - /opt/gomodel/data:/app/data +``` + +Start or update the service: + +```bash +docker compose up -d +docker compose logs -f gomodel +``` + +For multi-instance deployments, switch storage to PostgreSQL or MongoDB: + +```bash +STORAGE_TYPE=postgresql +POSTGRES_URL=postgres://gomodel:strong-password@postgres:5432/gomodel +POSTGRES_MAX_CONNS=10 +``` + +## Run as a native binary + +Use a native binary when you already manage services with systemd and want to +avoid a container runtime. + +Build the binary: + +```bash +make build +sudo install -m 0755 bin/gomodel /usr/local/bin/gomodel +``` + +For a native binary, use a host filesystem path for SQLite: + +```bash +# /opt/gomodel/.env +PORT=8080 +LOG_FORMAT=json +GOMODEL_MASTER_KEY=replace-with-a-long-random-secret +OPENAI_API_KEY=sk-... +STORAGE_TYPE=sqlite +SQLITE_PATH=/var/lib/gomodel/gomodel.db +LOGGING_ENABLED=true +LOGGING_LOG_BODIES=false +LOGGING_RETENTION_DAYS=30 +``` + +Create a systemd service: + +```ini +[Unit] +Description=GoModel AI gateway +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=gomodel +Group=gomodel +WorkingDirectory=/opt/gomodel +EnvironmentFile=/opt/gomodel/.env +ExecStart=/usr/local/bin/gomodel +Restart=always +RestartSec=5 +NoNewPrivileges=true +PrivateTmp=true + +[Install] +WantedBy=multi-user.target +``` + +Enable it: + +```bash +sudo useradd --system --home /var/lib/gomodel --shell /usr/sbin/nologin gomodel +sudo mkdir -p /var/lib/gomodel /opt/gomodel +sudo chown gomodel:gomodel /var/lib/gomodel +sudo systemctl daemon-reload +sudo systemctl enable --now gomodel +sudo journalctl -u gomodel -f +``` + +## Verify the deployment + +Check liveness: + +```bash +curl -fsS http://127.0.0.1:8080/health +``` + +Check authenticated API access: + +```bash +curl -fsS http://127.0.0.1:8080/v1/models \ + -H "Authorization: Bearer replace-with-a-long-random-secret" +``` + +Send a smoke-test request: + +```bash +curl -fsS http://127.0.0.1:8080/v1/chat/completions \ + -H "Authorization: Bearer replace-with-a-long-random-secret" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4o-mini", + "messages": [{"role": "user", "content": "Reply with ok."}] + }' +``` + +## Operational notes + +- Put HTTPS, rate limits, IP allowlists, and request timeouts in your reverse + proxy or load balancer. +- Keep `PPROF_ENABLED=false` in production unless you expose it only on a + trusted internal network during an investigation. +- Keep `LOGGING_LOG_BODIES=false` unless your data handling policy allows full + prompt and response capture. +- Use [Configuration](/advanced/configuration) for the full environment + variable reference. +- Use [Prometheus Metrics](/guides/prometheus-metrics) if you want metrics + scraping; it is currently experimental. From ce5f402c40a8171879ebc8c992f355d666acbc0c Mon Sep 17 00:00:00 2001 From: "Jakub A. W" Date: Mon, 25 May 2026 12:29:28 -0700 Subject: [PATCH 2/3] docs(getting-started): explain Docker storage path --- docs/getting-started/solid-start.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/getting-started/solid-start.mdx b/docs/getting-started/solid-start.mdx index b197c228..e7b36e5c 100644 --- a/docs/getting-started/solid-start.mdx +++ b/docs/getting-started/solid-start.mdx @@ -63,6 +63,13 @@ ADMIN_ENDPOINTS_ENABLED=true ADMIN_UI_ENABLED=true ``` +`SQLITE_PATH` uses `/app/data/gomodel.db` for Docker because that path exists +inside the GoModel image. The image runs from `/app`, creates `/app/data` as a +writable directory for the nonroot container user, and the examples below mount +the host directory `/opt/gomodel/data` into that container path. This keeps +runtime state out of the image while preserving it on the host. The executable +stays at `/gomodel`, so mounting `/app/data` cannot replace the binary. + Protect the file on the host: ```bash From 5287989a5c183a113593a3f7bdcc9abf7e11407f Mon Sep 17 00:00:00 2001 From: "Jakub A. W" Date: Mon, 25 May 2026 18:01:45 -0700 Subject: [PATCH 3/3] docs(getting-started): clarify Docker env file path --- docs/getting-started/solid-start.mdx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/solid-start.mdx b/docs/getting-started/solid-start.mdx index e7b36e5c..facb1182 100644 --- a/docs/getting-started/solid-start.mdx +++ b/docs/getting-started/solid-start.mdx @@ -28,7 +28,8 @@ Start with these decisions: ## Create a production .env file -Create `/opt/gomodel/.env` or another path managed by your deployment system: +Create `/opt/gomodel/.env` on the host, or use another host path managed by +your deployment system: ```bash PORT=8080 @@ -63,12 +64,17 @@ ADMIN_ENDPOINTS_ENABLED=true ADMIN_UI_ENABLED=true ``` +`/opt/gomodel/.env` is a host file. Docker reads it through `--env-file` or +Compose `env_file` before the container starts, then passes the values as +environment variables. The file is not copied into the image. Keeping secrets on +the host lets you rotate credentials without rebuilding or republishing the +container image. + `SQLITE_PATH` uses `/app/data/gomodel.db` for Docker because that path exists inside the GoModel image. The image runs from `/app`, creates `/app/data` as a writable directory for the nonroot container user, and the examples below mount the host directory `/opt/gomodel/data` into that container path. This keeps -runtime state out of the image while preserving it on the host. The executable -stays at `/gomodel`, so mounting `/app/data` cannot replace the binary. +runtime state out of the image while preserving it on the host. Protect the file on the host: