Configuration
Settings are resolved in order: runtime (system.settings collection) > CLI flags > environment variables > config file > defaults. Config file search order: ./anvil.toml (local), then /etc/anvil/anvil.toml (system install on Linux/macOS) or %LOCALAPPDATA%\Anvil\anvil.toml (Windows).
Runtime Settings (system.settings)
Most auth, email, storage, and observability knobs can be hot-reloaded through the system.settings document collection without restarting the server. Runtime values take the highest precedence and are persisted in the snapshot, so they survive restarts.
# Read all settings
curl http://localhost:7474/system/settings -H "Authorization: Bearer $JWT"
# Update one (admin only) - takes effect immediately
curl -X PUT http://localhost:7474/system/settings/auth.email.smtp_host \
-H "Authorization: Bearer $ADMIN_JWT" \
-d '{"value": "smtp.resend.com"}'
# Reset to file / env / default
curl -X DELETE http://localhost:7474/system/settings/auth.email.smtp_host \
-H "Authorization: Bearer $ADMIN_JWT"Or edit through the Hammer Admin page. Each setting carries category, type, description, and an audit trail (updated_at, updated_by).
System Install Paths
When installed via install.sh or install.ps1, Anvil uses system paths automatically:
| Linux / macOS | Windows | |
|---|---|---|
| Config | /etc/anvil/anvil.toml | %LOCALAPPDATA%\Anvil\anvil.toml |
| Data | /var/lib/anvil/ | %LOCALAPPDATA%\Anvil\data\ |
| Logs | /var/log/anvil/anvil.log | %LOCALAPPDATA%\Anvil\log\anvil.log |
| Plugins | /var/lib/anvil/plugins/ | %LOCALAPPDATA%\Anvil\data\plugins\ |
For local development builds (cargo run), paths default to ./data, ./plugins, and stdout logging.
Generate Config
anvil config init # Write default anvil.toml anvil config # Print effective config
anvil.toml
[server] http_port = 7474 bolt_port = 7687 bind_address = "0.0.0.0" # tls_cert = "/etc/anvil/fullchain.pem" # tls_key = "/etc/anvil/privkey.pem" [storage] data_dir = "./data" document_storage = "unified" # or "split" [auth] enabled = true default_password = "anvil" access_token_ttl_secs = 3600 # 1 hour refresh_token_ttl_secs = 604800 # 7 days require_email_verification = false # RSA key pair auto-generated -> data/jwt_key.pem [auth.email] # Empty smtp_host disables email; verification / OTP become no-ops smtp_host = "" smtp_port = 465 # 465 = implicit TLS, 587 = STARTTLS smtp_user = "" smtp_pass = "" smtp_from = "" smtp_starttls = true # only consulted when port != 465 webhook_url = "" # optional HTTP webhook instead of SMTP otp_ttl_secs = 300 otp_max_attempts = 3 verification_ttl_secs = 86400 template_dir = "data/templates" [logging] level = "info" log_file = "" # empty = stdout (daemon uses data/anvil.log) slow_query_threshold_ms = 1000
TLS / HTTPS
Set tls_cert and tls_key in [server] to enable HTTPS. Both must point to PEM files.
[server] tls_cert = "/etc/letsencrypt/live/example.com/fullchain.pem" tls_key = "/etc/letsencrypt/live/example.com/privkey.pem" # Or via environment variables: # ANVIL_TLS_CERT=/etc/anvil/fullchain.pem # ANVIL_TLS_KEY=/etc/anvil/privkey.pem
Environment Variables
Server & storage core
| Variable | Default |
|---|---|
| ANVIL_HTTP_PORT | 7474 |
| ANVIL_BOLT_PORT | 7687 |
| ANVIL_DATA_DIR | ./data |
| ANVIL_LOG_LEVEL | info |
| ANVIL_LOG_FILE | (empty = stdout) |
| ANVIL_AUTH_ENABLED | true |
| ANVIL_DOCUMENT_STORAGE | unified |
| ANVIL_SLOW_QUERY_THRESHOLD | 1000 |
| ANVIL_TLS_CERT | (not set) |
| ANVIL_TLS_KEY | (not set) |
File storage (Phase 25)
| Variable | Default | Notes |
|---|---|---|
| ANVIL_STORAGE_BACKEND | local | local | s3 | gcs | azure |
| ANVIL_STORAGE_LOCAL_ROOT | ./data/storage | Content-addressed blob root |
| ANVIL_STORAGE_STAGING_DIR | ./data/staging | Temp uploads |
| ANVIL_STORAGE_SIGNING_KEY_PATH | ./data/storage_signing_key.pem | HMAC key for signed URLs |
| ANVIL_STORAGE_SIGNED_URL_DEFAULT_TTL | 1h | Used when expires_in=0 |
| ANVIL_STORAGE_SIGNED_URL_MAX_TTL | 24h | Server-side clamp |
| ANVIL_STORAGE_UPLOAD_CHUNK_SIZE | 5MiB | TUS chunk advertised in OPTIONS |
| ANVIL_STORAGE_UPLOAD_EXPIRY | 24h | Resumable session lifetime |
| ANVIL_STORAGE_LOG_OBJECT_ACCESS | false | Emit StorageObjectAccessed events |
| ANVIL_STORAGE_MAX_FILE_SIZE | 50MiB | Per-object hard ceiling |
| ANVIL_STORAGE_MAX_TOTAL_STORAGE | 0 | 0 = no global cap |
| ANVIL_STORAGE_MAX_USER_STORAGE | 0 | Per-:auth.User cap; 0 = unlimited |
| ANVIL_STORAGE_IMAGE_CACHE_DIR | ./data/image_cache | Variant cache root |
| ANVIL_STORAGE_IMAGE_MAX_CACHE_SIZE | 2GiB | On-disk cache budget |
| ANVIL_STORAGE_IMAGE_MAX_DIMENSION | 4096 | Reject larger width/height |
| ANVIL_STORAGE_IMAGE_ALLOWED_FORMATS | webp,jpeg,png,avif | Output encoder allow-list |
S3 / R2 / MinIO (when ANVIL_STORAGE_BACKEND=s3)
| Variable | Notes |
|---|---|
| ANVIL_S3_KEY | Access key ID |
| ANVIL_S3_SECRET | Secret access key |
| ANVIL_S3_REGION | AWS region (e.g. us-east-1) |
| ANVIL_S3_BUCKET | Backing bucket name |
| ANVIL_S3_ENDPOINT | Custom endpoint for R2 / MinIO / Wasabi |
CLI Commands
anvil start # Daemon mode (logs to data/anvil.log) anvil start --foreground # Foreground mode anvil stop # Graceful shutdown anvil status # Show running state anvil start --http-port 8080 # Override config anvil import cypher ./data.cypher # Import Cypher script