7. Deploying jmaple¶
You've built a capability and it works locally. Now ship it.
Build a wheel¶
uv build produces a wheel and an sdist in dist/:
Pick a database¶
SQLite is fine for development but won't survive production. Use PostgreSQL:
The async driver asyncpg ships as a dependency.
Run with workers¶
uv run jmaple serve defaults to one worker for dev. In production, hand off
to a process manager — gunicorn with uvicorn workers is the common choice:
gunicorn jmaple.app:app \
-k uvicorn.workers.UvicornWorker \
-w 4 \
-b 0.0.0.0:8000 \
--access-logfile -
Run migrations on deploy¶
Run this before starting the server, ideally as a step in your deploy pipeline. Bundle your plugin's Alembic revision in the same package so operators run one command, not two.
Configure auth¶
For an external IdP, use the JWT or OIDC provider:
JMAPLE_AUTH__PROVIDERS=["jwt"]
JMAPLE_AUTH__JWT__ISSUER=https://issuer.example.com/
JMAPLE_AUTH__JWT__AUDIENCE=jmaple
JMAPLE_AUTH__JWT__JWKS_URL=https://issuer.example.com/.well-known/jwks.json
Bootstrap the first admin by listing their composite id:
The first time alice authenticates, the framework auto-provisions her user
record and grants her the admin role on the system account.
Blob storage¶
The default is the filesystem (JMAPLE_BLOB_DIR=./var/blobs). For
multi-instance deployments, wrap or replace jmaple.blobs.store.BlobStore with
an S3-backed implementation. The protocol is small: put, open, delete.
Observability¶
- The framework uses Python's
loggingmodule — configure handlers as you would any FastAPI app. - Every JMAP method call writes structured log lines including the method name and call id.
- Failed handlers log full tracebacks at WARNING; request-level errors get ERROR.
What's behind a load balancer¶
/jmapand/.well-known/jmap— JSON request/response, normal HTTP./jmap/upload/*— POST with arbitraryContent-Type, no chunked encoding required./jmap/download/*— GET, streams the blob./jmap/eventsource— long-livedtext/event-stream, setproxy_buffering offin Nginx./jmap/ws— WebSocket upgrade.
That's everything you need to know to ship jmaple. The topic guides go deeper on the subsystems you touched in the tutorial.