This example shows the application seams (plugin, branding). This doc covers the operational part — getting a real, persistent deployment right. It's provider-agnostic; adapt the specifics to your cloud.
The one thing to get right is persistence. Everything else (build the image, run it behind TLS) is standard.
The backend keeps all state in a single database and holds some in-memory
state (chat sessions) inside the process. By default that database is SQLite
(LAMBDA_ERP_DB points at a file). This shapes two hard rules below.
SQLite needs real filesystem file locks. That has a sharp edge in the cloud:
- ✅ A real local/block disk (a VM's managed disk, a bare-metal volume): SQLite
works perfectly. Point
LAMBDA_ERP_DBat a file on it and you're done. - ❌ A network file share (SMB/NFS "Azure Files", NFS mounts, many container
platforms' only persistent-volume option): SQLite's locking does not work
reliably. You'll get
database is lockedat startup —PRAGMA journal_mode=WALfails outright (WAL needs a memory-mapped-shmfile a network FS can't provide), and even plain writes fail on SMB. Do not put the SQLite file on a network share.
So on a platform whose only durable storage is a network share (e.g. Azure Container Apps, many PaaS container hosts), don't fight SQLite — use PostgreSQL:
pip install lambda-erp[postgres] # pulls psycopg
export LAMBDA_ERP_DB="postgresql://USER:PASSWORD@HOST:5432/DBNAME?sslmode=require"lambda-erp (>= 0.1.5) selects the backend from LAMBDA_ERP_DB: a
postgresql://… value uses Postgres; anything else is treated as a SQLite path.
No code change — same app, same seams. Put the URL (it contains a password) in a
secret, not a plaintext env var.
Rule of thumb:
- VM / box with a disk → SQLite is fine and simplest.
- Managed container platform → Postgres.
SQLite is single-writer, and the chat session state lives in the process. Run
a single replica with --workers 1. Scale up (more CPU/RAM), not out.
(Postgres removes the SQLite-writer limit, but the in-memory chat state still
pins you to one replica until that moves to a shared store.)
Set JWT_SECRET_KEY and keep it constant across deploys — a new value
invalidates every login cookie. If unset, the app generates one, which is fine
for local dev but means everyone is logged out on each restart.
One container, behind TLS, with the DB external:
docker build -t my-erp .
docker run -p 8000:8000 \
-e LAMBDA_ERP_PLUGINS=acme \
-e LAMBDA_ERP_DB="postgresql://erp:…@db.internal:5432/erp?sslmode=require" \
-e JWT_SECRET_KEY="<stable-random>" \
-e OPENAI_API_KEY=… -e ANTHROPIC_API_KEY=… \
my-erpFor a throwaway demo, ephemeral SQLite on the container's own disk is fine — it just resets on each deploy. For anything you care about, follow Rule 1.
- Postgres: use your provider's automated backups / point-in-time restore.
- SQLite on a disk: snapshot the volume, or copy the DB file (use the SQLite
backup API /
VACUUM INTOfor a consistent copy while running).