Configuration (.env)
# ── Environment ───────────────────────────────────────────────────
# development → debug, hot reload and pprof enabled automatically
# production → optimized mode, debug disabled
APP_ENV=development
# ── Server ────────────────────────────────────────────────────────
SERVER_HOST=0.0.0.0 # bind — see the note below before opening it in a browser
SERVER_PORT=8000
SERVER_WORKERS=4 # omit to use all available CPUs
# ── Allowed hosts ─────────────────────────────────────────────────
# Ignored in development. Required in production.
ALLOWED_HOSTS=mysite.com,www.mysite.com
# ── Canonical URL (optional) ───────────────────────────────────────
# No trailing slash. Used by the built-in "meta" app (apps/meta) to build
# absolute URLs in robots.txt, sitemap.xml and security.txt — see SEO &
# Security.txt. Empty until configured; in that case those files ship
# without a domain (still valid, just incomplete).
# APP_URL=https://mysite.com
# ── Timezone (optional) ──────────────────────────────────────────────
# IANA name used by the admin to DISPLAY automatic timestamps
# (created_at/updated_at, history log) — never affects what's written to
# the database (TIMESTAMPTZ columns store the absolute instant). Defaults
# to UTC if unset; an invalid name falls back to UTC with a log warning,
# never blocks boot.
# APP_TIMEZONE=America/Sao_Paulo
# ── Database ──────────────────────────────────────────────────────
# Each DB_NAME block starts a new database. The first one is the default.
DB_NAME=main
DB_ENABLED=true
DB_DRIVER=postgres
DB_DSN=postgres://user:password@localhost:5432/mydb?sslmode=disable
# DB_NAME=analytics
# DB_ENABLED=true
# DB_DRIVER=postgres
# DB_DSN=postgres://user:password@localhost:5432/analytics?sslmode=disable
# ── Cache ─────────────────────────────────────────────────────────
CACHE_ENABLED=false
CACHE_DRIVER=memory
CACHE_ADDR=localhost:6379
# ── Queue (background task queue) ──────────────────────────────────
QUEUE_ENABLED=false
QUEUE_DRIVER=memory # memory | redis (roadmap)
QUEUE_ADDR=localhost:6379
QUEUE_WORKERS=4
# ── Admin (opt-in panel per model — requires admin.Register[T] in code)
ADMIN_ENABLED=false
ADMIN_PATH=/admin/
# Optional — creates the initial superuser at boot, if no one with this
# login already exists (does not reset the password of an existing account)
# ADMIN_SUPERUSER_USERNAME=admin
# ADMIN_SUPERUSER_PASSWORD=change-this-temporary-password
# ── Security ──────────────────────────────────────────────────────
SECRET_KEY=your-strong-secret-key-here
SESSION_TTL=3600 # session duration in seconds
# Pepper applied before the Argon2id hash — never store it in the database
# Generate with: openssl rand -base64 32
PASSWORD_PEPPER=your-strong-pepper-here
# AES-256-GCM key for kyrux:"encrypt" fields — never store it in the database
# Generate with: openssl rand -base64 32
FIELD_ENCRYPTION_KEY=your-strong-encryption-key-here
# ── Runtime (optional) ────────────────────────────────────────────
# Go's GC percentage. Default: 100. Lowering it (e.g. 75) reduces heap size, increases GC frequency.
# RUNTIME_GOGC=75
In production,
SECRET_KEY,PASSWORD_PEPPER,FIELD_ENCRYPTION_KEY, andALLOWED_HOSTSare required — the server refuses to start without them.
SERVER_HOST=0.0.0.0 is the bind address, not the access address
0.0.0.0 tells the server to listen on all of the machine's network
interfaces — it isn't a real address a browser can connect to. Typing
http://0.0.0.0:8000 in the address bar results in ERR_CONNECTION_REFUSED
(or inconsistent behavior, depending on the OS).
| Address | What it is | Use |
|---|---|---|
0.0.0.0 |
Wildcard for "all interfaces" | Only for SERVER_HOST (bind) |
127.0.0.1 |
Loopback — this machine, always present and routable | Access locally |
localhost |
Name the OS resolves to 127.0.0.1 (or ::1) |
Access locally |
To access a locally running server, always use http://127.0.0.1:PORT
or http://localhost:PORT — never 0.0.0.0, even with
SERVER_HOST=0.0.0.0 configured (the bind still applies to all interfaces;
only the access address changes). Kyrux already translates this on its own
in the messages it prints (console, welcome page, debug dashboard) — they
all show 127.0.0.1, never 0.0.0.0.