Progressive Infrastructure
Appstrate's 4-tier infrastructure model, from zero-install to full production deployment.
The Progressive Model
Appstrate resolves its infrastructure adapters at boot based on which environment variables are set. Each missing dependency is replaced by an in-process fallback, so you can start with zero external services and promote piece by piece.
Infrastructure adapters live in apps/api/src/infra/ and are loaded via dynamic imports driven by hasExternalDb(), hasRedis(), hasS3(), and getExecutionMode() in infra/mode.ts.
The 4 Tiers
Tier 0 — Zero-install
| Component | Implementation when tier condition met |
|---|---|
| Database | PGlite (embedded WASM PostgreSQL, ./data/pglite/) |
| Storage | Local filesystem at FS_STORAGE_PATH (default ./data/storage) |
| Queue / scheduler | LocalQueue — a custom in-process cron evaluator, not BullMQ |
| PubSub | LocalPubSub — Node EventEmitter |
| Rate limiter | rate-limiter-flexible in-memory (RateLimiterMemory) |
| Run execution | Bun subprocess on the host (RUN_ADAPTER=process, the default) |
No external dependencies. Ideal for development and quick evaluation.
cp .env.example .env && bun run devTier 1 — PostgreSQL
Set DATABASE_URL to switch to PostgreSQL. Data becomes persistent and multi-user works properly. Queue, PubSub, rate limiter, and storage stay in-process.
bun run docker:dev:minimalTier 2 — PostgreSQL + Redis
Set REDIS_URL on top of Tier 1 to unlock:
- BullMQ (Redis-backed) for the scheduler queue
- Redis PubSub for realtime and cross-instance run cancellation
- Redis-backed distributed rate limiting via
rate-limiter-flexible'sRateLimiterRedis - Horizontal scaling: multiple Appstrate instances can share the same Redis and distribute work
bun run docker:dev:standardTier 3 — Object storage and/or container isolation
Tier 3 adds two independent capabilities on top of Tier 2:
S3_BUCKETroutes storage to S3 or MinIO. Without it, storage stays on the filesystem atFS_STORAGE_PATH.RUN_ADAPTER=dockerruns each agent inside an ephemeral Docker container on an internal-only network, with a per-run sidecar for credential injection. Without it, agents run as Bun subprocesses on the host (no isolation). See Sandbox & Sidecar for the isolation model.
RUN_ADAPTER=docker additionally requires the Docker daemon socket to be accessible (DOCKER_SOCKET, default /var/run/docker.sock).
bun run docker:devSummary Table
| Tier | Added services | In-process fallbacks still in use | Use case |
|---|---|---|---|
| 0 | Bun only | PGlite, LocalQueue, LocalPubSub, RateLimiterMemory, filesystem storage, Bun subprocess | Development, evaluation |
| 1 | + PostgreSQL | LocalQueue, LocalPubSub, RateLimiterMemory, filesystem storage, Bun subprocess | Persistent data, multi-user |
| 2 | + Redis | Filesystem storage, Bun subprocess | Scheduling, distributed rate limiting, horizontal scaling |
| 3 | + S3/MinIO and/or Docker | Depends on which axis you enable | Full prod with isolation and/or object storage |
Migrating between tiers
Promoting from Tier 0 to Tier 1+ means swapping the database backend from PGlite to PostgreSQL. There is no PGlite dump REST endpoint today — you have two practical paths:
- Fresh start (recommended if you have no production data yet): stop the Tier 0 instance, set
DATABASE_URL, restart. Drizzle migrations run automatically at boot and create an empty schema in the new PostgreSQL database. - Copy data manually: stop the Tier 0 instance, export each table from PGlite with a script that reads the local PGlite data directory, then import into PostgreSQL. PGlite and PostgreSQL share a schema, so
pg_dump-compatible output can be restored withpsql "$DATABASE_URL" -f backup.sql. Plan for downtime during the swap.
Promoting Tier 1 → Tier 2 (adding Redis) or Tier 2 → Tier 3 (adding S3/Docker) is non-destructive: the adapters swap at the next boot, no data migration needed.
Drizzle migrations run automatically at every boot for both PGlite and PostgreSQL. Schema evolution within a tier is handled; there is no built-in rollback path.
Other boot behaviors worth knowing
- Orphan run cleanup: on startup, any run still in
runningorpendingfrom a previous process is markedfailed, and Docker containers labeledappstrate.managed=trueare removed. - System packages sync:
system-packages/*.afpsZIPs are loaded and synced to the database withorgId: nullon every boot. - Sidecar pool warm-up: when
RUN_ADAPTER=docker,SIDECAR_POOL_SIZEpre-warmed sidecar containers are created at boot (default 2, set 0 to disable).
Recommendation
For production, use Tier 3 (PostgreSQL + Redis + S3/MinIO + Docker execution) behind a TLS reverse proxy. Tier 0 is the fastest way to evaluate Appstrate without installing anything. Tiers 1 and 2 are suitable for staging or shared development environments.