Skip to content

Configuration Reference

All tenement configuration lives in a single tenement.toml file.

Global configuration for the tenement server.

[settings]
data_dir = "/var/lib/tenement" # Base data directory
health_check_interval = 10 # Health check interval (seconds)
max_restarts = 3 # Max restarts within window
restart_window = 300 # Restart window (seconds)
backoff_base_ms = 1000 # Exponential backoff base (1s)
backoff_max_ms = 60000 # Max backoff delay (60s)
FieldTypeDefaultDescription
data_dirstring/var/lib/tenementBase directory for instance data
health_check_intervalint10Seconds between health checks
max_restartsint3Max restarts before giving up
restart_windowint300Window for counting restarts (seconds)
backoff_base_msint1000Initial backoff delay (ms)
backoff_max_msint60000Maximum backoff delay (ms)

Define services that tenement can spawn. Each service is a template for instances.

[service.api]
command = "uv run python app.py" # Command to run
args = ["--port", "8000"] # Optional command arguments
workdir = "/app" # Optional working directory
socket = "/tmp/tenement/{name}-{id}.sock" # Socket path template (default)
health = "/health" # Health check endpoint
startup_timeout = 10 # Max seconds to create socket
idle_timeout = 300 # Auto-stop after N seconds idle
restart = "on-failure" # Restart policy
isolation = "namespace" # Isolation level (alias: runtime)
# Resource limits (cgroups v2, Linux only)
memory_limit_mb = 256 # Memory limit in MB
cpu_shares = 100 # CPU weight (1-10000)
# Storage quotas
storage_quota_mb = 100 # Max storage per instance (MB)
storage_persist = false # Persist data on stop (default: false)
FieldTypeDefaultDescription
commandstringrequiredShell command to start the service
argsarray[]Optional command arguments
workdirstringnoneWorking directory for the process
socketstring/tmp/tenement/{name}-{id}.sockUnix socket path template
healthstringnoneHTTP path for health checks
startup_timeoutint10Max seconds to wait for socket
idle_timeoutint0Stop after N seconds idle (0 = never)
restartstringon-failureRestart policy: always, on-failure, never
isolationstringnamespaceIsolation level (alias: runtime)
memory_limit_mbintnoneMemory limit in MB (Linux cgroups v2)
cpu_sharesintnoneCPU weight 1-10000 (Linux cgroups v2)
storage_quota_mbintnoneMax disk usage per instance (MB)
storage_persistboolfalseKeep data directory on instance stop
ValueDescriptionOverheadUse Case
processNo isolation~0Debugging, trusted code
namespacePID + Mount namespace~0Default - multi-tenant trusted code
sandboxgVisor syscall filtering~20MBUntrusted/third-party code
firecrackerFirecracker microVM~128MBFull VM isolation (requires KVM)
qemuQEMU VM~128MBCross-platform VM (requires KVM/HVF)

Note: The runtime field is accepted as an alias for isolation for backwards compatibility.

ValueBehavior
alwaysAlways restart on exit
on-failureRestart only on non-zero exit
neverNever restart

Per-service environment variables with template support.

[service.api.env]
DATABASE_PATH = "{data_dir}/{id}/app.db"
LOG_LEVEL = "info"
SECRET_KEY = "${SECRET_KEY}" # From environment

Template variables:

  • {name} - Service name (e.g., “api”)
  • {id} - Instance ID (e.g., “prod”)
  • {data_dir} - The data_dir from settings
  • {socket} - The resolved socket path
  • {port} - Auto-allocated TCP port (30000-40000 range)
  • ${VAR} - Value from host environment

Auto-set environment variables:

  • PORT - TCP port allocated for the instance
  • SOCKET_PATH - Unix socket path for the instance

Auto-spawn instances on ten serve startup.

[instances]
api = ["prod", "staging"] # Spawn api:prod and api:staging
worker = ["default"] # Spawn worker:default

Each key is a service name, value is an array of instance IDs to spawn.

Behavior:

  • Validates that referenced services exist at config load time
  • Individual spawn failures are logged but don’t block others
  • Instances spawn after the server starts listening

Configure custom routing rules beyond the default subdomain-based routing.

[routing]
default = "api" # Default service for root domain
[routing.subdomain]
"admin" = "admin-service" # admin.example.com → admin-service
[routing.path]
"/api" = "api-service" # example.com/api/* → api-service
"/docs" = "docs-service" # example.com/docs/* → docs-service
FieldTypeDescription
defaultstringService to route root domain requests to
subdomainmapSubdomain → service name mappings
pathmapPath prefix → service name mappings

Default routing (without config):

  • {id}.{service}.{domain} → routes to specific instance
  • {service}.{domain} → weighted routing across all instances of service
  • {domain} → dashboard

Configure automatic HTTPS with Let’s Encrypt certificates.

[settings.tls]
enabled = true
acme_email = "admin@example.com" # Required for Let's Encrypt
domain = "example.com"
cache_dir = "/var/lib/tenement/acme" # Certificate cache (default: {data_dir}/acme)
staging = false # Use staging for testing (avoids rate limits)
https_port = 443
http_port = 80
dns_provider = "cloudflare" # For wildcard certs via Caddy
FieldTypeDefaultDescription
enabledboolfalseEnable TLS
acme_emailstringnoneEmail for Let’s Encrypt registration
domainstringnoneDomain for TLS certificate
cache_dirstring{data_dir}/acmeDirectory for certificate cache
stagingboolfalseUse Let’s Encrypt staging environment
https_portint443HTTPS listening port
http_portint80HTTP port (redirects + ACME challenges)
dns_providerstringnoneDNS provider for Caddy wildcard certs

CLI flags take precedence:

Terminal window
ten serve --tls --domain example.com --email admin@example.com --staging
[settings]
data_dir = "/var/lib/myapp"
health_check_interval = 10
max_restarts = 3
restart_window = 300
[settings.tls]
enabled = true
acme_email = "admin@example.com"
domain = "example.com"
[service.api]
command = "uv run python app.py"
health = "/health"
idle_timeout = 300
restart = "on-failure"
isolation = "namespace"
memory_limit_mb = 256
cpu_shares = 100
storage_quota_mb = 100
[service.api.env]
DATABASE_PATH = "{data_dir}/{id}/app.db"
LOG_LEVEL = "info"
[service.worker]
command = "./worker"
health = "/health"
isolation = "namespace"
memory_limit_mb = 512
cpu_shares = 200
[instances]
api = ["prod", "staging"]
worker = ["default"]

Some config values can be overridden via CLI flags:

Terminal window
ten serve --port 8080 # Override listen port
ten serve --domain example.com # Set domain for routing
ten serve --tls --email you@x.com # Enable TLS with ACME
# Use different config file via environment variable
TENEMENT_CONFIG=custom.toml ten serve