Configuration
All settings are stored in config.yaml. Copy the example to get started:
cp config.example.yaml config.yamlSettings can also be overridden via environment variables with the STOA_ prefix:
STOA_DATABASE_URL="postgres://user:pass@host:5432/db?sslmode=require"
STOA_AUTH_JWT_SECRET="a-secure-secret"
STOA_SERVER_PORT=8080Key Settings
| Setting | Default | Description |
|---|---|---|
server.port | 8080 | HTTP port |
database.url | postgres://stoa:secret@localhost:5432/stoa | PostgreSQL connection string |
auth.jwt_secret | (required, min 32 bytes) | JWT signing key — see JWT Secret Validation |
media.storage | local | Media storage (local or s3) |
media.local_path | ./uploads | Local upload path |
i18n.default_locale | de-DE | Default language |
server.max_body_size | 1048576 (1 MB) | Max request body size for JSON endpoints (bytes) |
server.max_upload_size | 36700160 (35 MB) | Max request body size for multipart/file uploads (bytes) |
payment.encryption_key | (required) | AES-256 key for payment config encryption (32 bytes or 64 hex chars) |
security.rate_limit.requests_per_minute | 300 | Global IP-based rate limit |
security.rate_limit.login.requests_per_minute | 10 | Login endpoint rate limit per IP |
security.rate_limit.register.requests_per_minute | 5 | Register endpoint rate limit per IP |
security.rate_limit.checkout.requests_per_minute | 10 | Checkout endpoint rate limit per IP |
security.rate_limit.guest_order.requests_per_minute | 10 | Guest order lookup rate limit per IP |
Request Body Limits
Stoa limits incoming request body sizes to prevent memory exhaustion from oversized payloads. Two tiers are applied automatically based on Content-Type:
- JSON / default —
server.max_body_size(default: 1 MB). Applied to allPOST,PUT,PATCH, andDELETErequests that are not multipart. - Multipart / uploads —
server.max_upload_size(default: 35 MB). Applied whenContent-Typestarts withmultipart/form-data.
GET, HEAD, and OPTIONS requests are not affected.
Requests exceeding the limit receive a 413 Request Entity Too Large response with error code body_too_large. HTTP request headers are additionally capped at 1 MB via MaxHeaderBytes.
Override via environment variables:
STOA_SERVER_MAX_BODY_SIZE=2097152 # 2 MB
STOA_SERVER_MAX_UPLOAD_SIZE=52428800 # 50 MBOr in config.yaml:
server:
max_body_size: 2097152
max_upload_size: 52428800Payment Encryption Key
The payment.encryption_key is required to encrypt provider credentials (API keys, secrets) stored in payment methods. Stoa validates this key at startup before connecting to the database — if the key is missing or has an invalid length, the server exits immediately with a descriptive error message.
The key must be exactly 32 bytes (raw) or 64 hex characters:
# Generate a random 64-character hex key
openssl rand -hex 32Set via environment variable:
STOA_PAYMENT_ENCRYPTION_KEY="your-64-char-hex-key"If the key is missing or invalid, you'll see an error like:
invalid configuration: payment.encryption_key is required — it protects
stored payment credentials. Generate one with: openssl rand -hex 32WARNING
Never commit your config.yaml to version control if it contains real secrets. Use environment variables in production.
Database SSL/TLS
The database.url connection string supports a sslmode parameter that controls whether the connection to PostgreSQL is encrypted:
| Mode | Description |
|---|---|
disable | No TLS — credentials and data sent in plaintext |
require | TLS required — connection fails if the server doesn't support it |
verify-ca | TLS + server certificate verified against a CA |
verify-full | TLS + server certificate verified + hostname match |
The default in config.example.yaml is sslmode=require. Use sslmode=disable only for local development (e.g. Docker Compose with a local PostgreSQL container).
database:
url: "postgres://stoa:secret@localhost:5432/stoa?sslmode=require"Stoa logs a warning at startup when sslmode=disable is detected:
WRN database connection uses sslmode=disable — not recommended for productionWARNING
Never use sslmode=disable in production. Even within private networks, TLS protects against credential sniffing and man-in-the-middle attacks.
CLI Reference
stoa serve # Start HTTP server
stoa migrate up # Run migrations
stoa migrate down # Roll back last migration
stoa admin create # Create admin user (interactive password prompt)
--email admin@example.com
stoa seed --demo # Load demo data
stoa plugin list # List installed plugins
stoa version # Print version