Production Checklist
What to verify before taking an Appstrate instance live.
The defaults suit local development. Going live takes a few deliberate decisions. Walk through this list before you open the instance to real users. Variables are described in Environment Variables, and appstrate install already does several of these steps for you.
Choose the stack
- Single node: Tier 2 (PostgreSQL and Redis, files on a persisted volume) with
RUN_ADAPTER=docker. This is the installer's recommended stack. - Several platform instances, or object storage: add S3-compatible storage (
S3_BUCKET,S3_REGION,S3_ENDPOINTfor MinIO or R2) and point every instance at the same PostgreSQL, Redis and bucket. Bundled MinIO is Tier 3. - Hostile or multi-tenant workloads: consider
RUN_ADAPTER=firecrackerfor a microVM per run. - Do not use Tier 0, Tier 1 or
RUN_ADAPTER=processin production. They give up persistence, shared state or isolation. See Progressive Infrastructure.
Terminate TLS and set the public URL
Appstrate does not terminate HTTPS. Put a reverse proxy in front (nginx, Caddy, Traefik, or a cloud load balancer) and set:
APP_URL=https://appstrate.example.com
TRUSTED_ORIGINS=https://appstrate.example.com
TRUST_PROXY=1With NODE_ENV=production, APP_URL must be HTTPS (loopback excepted), and an HTTPS APP_URL with TRUST_PROXY=false refuses to boot. TRUST_PROXY is the number of proxies that append to X-Forwarded-For, and a value higher than your topology lets callers spoof their IP. Raise the proxy's body limit above your largest upload (100 MiB by default) and do not buffer or compress event streams. See Docker Compose.
Generate and protect the secrets
Five secrets are required, and each rotates independently:
| Variable | Format | Impact of changing it |
|---|---|---|
BETTER_AUTH_SECRET | Random string | Changing it in place leaves what was encrypted under it (the keys that sign CLI and OIDC tokens) unreadable. Rotate through the BETTER_AUTH_SECRETS keyring instead, which signs everyone out |
CONNECTION_ENCRYPTION_KEY | 32 bytes, base64 | Stored credentials become unreadable unless you rotate with a key id. See the rotation procedure in Environment Variables |
UPLOAD_SIGNING_SECRET | At least 16 characters | Invalidates in-flight upload tokens |
RUN_TOKEN_SECRET | At least 16 characters | Invalidates tokens of in-flight runs |
CONNECT_SESSION_SECRET | At least 16 characters | Invalidates in-flight connect sessions |
The installer generates them. If you do it by hand, use openssl rand -hex 32 for the first four and openssl rand -base64 32 for the encryption key. Keep them in a secret manager, never in git, and keep an offline copy of CONNECTION_ENCRYPTION_KEY: without it, no backup of the database can decrypt the credentials it holds.
Close the instance
Open mode lets anyone with the URL create an account and an organization. For a private deployment, run in closed mode: AUTH_DISABLE_SIGNUP, AUTH_DISABLE_ORG_CREATION, AUTH_PLATFORM_ADMIN_EMAILS and a one-time AUTH_BOOTSTRAP_TOKEN, claimed at <APP_URL>/claim. The installer sets this up when you give it an owner email or run unattended. Then:
- Remove
AUTH_BOOTSTRAP_TOKENfrom.envonce the instance is claimed. - Check that you can sign in to every address listed in
AUTH_BOOTSTRAP_OWNER_EMAILandAUTH_PLATFORM_ADMIN_EMAILS. - If your Compose file predates 1.0.0-beta.65, confirm it forwards
AUTH_BOOTSTRAP_TOKENto the container. The current files do (see Docker Compose).
The full recipes and pitfalls are in AUTH_MODES.md.
Restrict CORS
TRUSTED_ORIGINS is a comma-separated list of the exact origins of your dashboards and integrations. The shipped Compose files leave it empty, and an empty value falls back to http://localhost:3000,http://localhost:5173, so set it explicitly.
Review the limits
The defaults are conservative: 200 run launches per minute and 50 concurrent runs per organization, a 30 minute run ceiling, and 1536 MiB and 2 vCPU per run. Override only what you need, in PLATFORM_RUN_LIMITS and INLINE_RUN_LIMITS. Lower timeout_ceiling_seconds if your agents never need 30 minutes. Pair rate limiting with Redis, otherwise limits are per process and reset on restart. See Rate Limits.
Pin image versions
Pin one release for every image:
APPSTRATE_VERSION=1.0.0-beta.65Tags have no v prefix. APPSTRATE_VERSION is a Compose variable that moves the platform, the agent runtime, the sidecar and the MCP runner images together, and the platform refuses to boot if the platform and runtime image versions disagree. If you run your own Compose file, pin all of them yourself. Read Upgrading before changing the pin.
Configure model providers
Appstrate needs at least one LLM provider. Either set SYSTEM_PROVIDER_KEYS (a JSON array of providers with their models, injected by the sidecar and never exposed to agents) or let organizations add their own. A model on a private endpoint needs its host in EGRESS_ALLOW_INTERNAL_HOSTS.
Review egress
Agents can only reach hosts that an integration's authorized_uris allows, and private ranges are blocked. If an integration must call an internal API, add its host to EGRESS_ALLOW_INTERNAL_HOSTS. That is an instance-wide trust decision, so list only hosts that every organization may use. The sidecar needs working DNS. See Isolation and Security.
The API process itself also makes outbound requests to get.appstrate.dev to read the live model catalog. On a network that must stay closed, set MODEL_CATALOG_URL=off (see Isolation and Security).
Configure email
Email verification and magic-link sign-in need SMTP. Set SMTP_HOST, SMTP_USER, SMTP_PASS and SMTP_FROM together (SMTP_PORT defaults to 587): mail is enabled only when all four are set, and no error is raised when they are not. Test with a magic link or a password reset before go-live.
With SMTP on, the instance also mails account notices: a password change or reset is reported to the account's address, a sign-up with an address that already has an account is answered at that address, and an email change must be approved from the current address before anything is sent to the new one. Without SMTP, an email change applies at once, with no confirmation.
The Compose files shipped since 1.0.0-beta.65 forward the SMTP and the Google and GitHub sign-in variables. The tier templates of earlier releases do not, so with an older file check that they are listed under appstrate.environment (see Docker Compose).
Choose your modules
MODULES defaults to oidc,webhooks,mcp,core-providers,@appstrate/module-chat. Drop what you do not use to reduce the attack surface, but keep oidc if you use the CLI. Opt-in modules (firecracker, @appstrate/module-observability, the two subscription providers, @appstrate/module-ee) are listed in Progressive Infrastructure.
Protect the Docker socket
The docker backend needs the socket, and access to it is root on the host. Keep it root:docker 0660, run the platform with the host's docker group (DOCKER_GID in the tier templates), and read the socket-proxy notes in the root docker-compose.yml. See Isolation and Security.
Set up monitoring
- Health.
GET /healthreturnshealthy,degraded,unhealthyorstartingwith per-subsystem checks, answering503when the database is unhealthy or the server is still starting.GET /serves the dashboard and is not a health signal. - Logs. The platform writes structured JSON to stdout.
LOG_LEVEL=infois the default. Usedebugduring incidents (it adds one access line per request), andwarnfor steady state. Forward the logs to your stack and correlate on theRequest-Idresponse header. - Traces and metrics. Append
@appstrate/module-observabilitytoMODULESand setOTEL_EXPORTER_OTLP_ENDPOINTto export OpenTelemetry over OTLP/HTTP. It is off by default. See OBSERVABILITY.md. - Log levels and messages change between releases. Check the operator notes before you rely on a log line for an alert.
Plan backups
Appstrate has no built-in backup tool. Use your existing playbook, and test a restore.
- PostgreSQL: a nightly logical dump (
pg_dump -Fc), kept off the host. This holds your users, agents, runs and encrypted credentials. - Secrets:
CONNECTION_ENCRYPTION_KEYand the other secrets, stored separately from the database dump. - Files: the
storagedatavolume on Tier 1 and 2, or the S3 bucket (versioning and replication) on Tier 3 and with your own S3. On Tier 3 the bundled MinIO volume isminiodata. - Redis: holds the queue behind scheduled runs, webhook deliveries and other background jobs, plus run-event buffers. The Compose files persist it on a volume. Keep that volume if losing queued work matters, or use a replicated Redis.
- A one-off script,
scripts/storage-orphans.ts, reconciles stored objects against database rows. Run it in dry-run mode first.
Legal links
Set LEGAL_TERMS_URL and LEGAL_PRIVACY_URL to show your policies in the footer.
Plan upgrades
Migrations run at boot, there is no built-in rollback, and some releases need operator steps before the deploy. Pin versions, rehearse in staging, take a database backup and read the operator notes of every release you skip. See Upgrading.
Sanity check before go-live
# 1. Health
curl https://appstrate.example.com/health
# 2. Auth is enforced (expect 401)
curl -i https://appstrate.example.com/api/agents
# 3. OpenAPI is served
curl https://appstrate.example.com/api/openapi.json | jq .infoThen, in the dashboard: sign in as the owner, add a model, run a trivial agent end to end, and confirm a signed-out visitor cannot create an account (in closed mode). If the three commands answer as expected and the agent run succeeds, you are production-ready.
Firecracker Execution Backend
Run each agent in its own microVM. What the opt-in Firecracker backend requires, how to install the runner daemon on a KVM host, how the platform pairs with it, and how to check it.
Monitoring and Observability
Check platform health, read the logs, follow one request by its Request-Id and export OpenTelemetry traces and metrics to your own collector.