Install
Docker
Run postkeep from the container image postkeep/postkeep.
docker run --rm postkeep/postkeep generate-key # keep this key safe
docker run -d --name postkeep -p 127.0.0.1:8080:8080 \
-v postkeep:/data \
-e POSTKEEP_PUBLIC_URL=https://mail.example.com \
-e POSTKEEP_MASTER_KEY=<the key> \
-e POSTKEEP_ALLOWED_EMAILS[email protected] \
postkeep/postkeepPut an HTTPS reverse proxy in front, or use the Docker Compose setup with
automatic HTTPS. The image is built for linux/amd64 and linux/arm64.
To build it yourself from the repository: docker build -t postkeep/postkeep ..
What every deployment needs
-
A public HTTPS URL. MCP clients and your browser must reach the server, and OAuth requires HTTPS outside
localhost. Terminate TLS in a reverse proxy (Caddy, nginx, Traefik) or use a host that provides it. SetPOSTKEEP_PUBLIC_URLto the origin only, such ashttps://mail.example.com, with no path. -
A master key. It encrypts stored app passwords, Microsoft and Google tokens, webhook secrets and stored webhook events (AES-256-GCM). Generate one and keep a backup:
docker run --rm postkeep/postkeep generate-key # or: postkeep generate-key, or: openssl rand -hex 32Pass it as
POSTKEEP_MASTER_KEY, or put it in a file and setPOSTKEEP_MASTER_KEY_FILE(works well with Docker, Kubernetes and systemd secrets). If the key is lost or changed, none of these can be decrypted: every user must connect again, and webhook secrets and queued events are lost. To change the key without that, rotate it (see configuration). -
Persistent storage. By default the server uses a SQLite file (
/data/postkeep.dbin the image), so mount a volume at/data. Upgrading from 0.2.x with the default SQLite path: 0.3.0 looks forpostkeep.db, so rename your existing database file topostkeep.db(in the image,/data/postkeep.db) or pointPOSTKEEP_DATABASE_URLat it before starting. Account-page users sign in again once. If 0.3.0 has already started and created an emptypostkeep.db, stop it and replace that file with your old one. For several replicas or hosts without disks, use PostgreSQL:POSTKEEP_DATABASE_URL=postgres://user:password@host/db. When the database is remote, add?sslmode=verify-full. That checks the server certificate and host name against the webpki roots compiled into the binary.sslmode=requireencrypts without that check. If the provider uses a private CA, also addsslrootcert=/path/ca.pem(a PEM file the process can read). The schema is created and migrated automatically at startup. Each instance opens at most 10 database connections (POSTKEEP_DATABASE_MAX_CONNECTIONS). Behind a proxy or pooler that closes connections, recycle them sooner. For example, Fly Managed Postgres recommends a 300-second idle timeout and a 600-second lifetime (Fly), so setPOSTKEEP_DATABASE_IDLE_TIMEOUT=300andPOSTKEEP_DATABASE_MAX_LIFETIME=600. These are examples, not defaults. Migrations run at start. Behind a transaction pooler, run them on a direct connection instead: setPOSTKEEP_DATABASE_MIGRATE_URLto it, runpostkeep migrateas a release command (it exits 0 when there is nothing to do), and setPOSTKEEP_DATABASE_MIGRATE_ON_START=falseso the server refuses to start on a schema it has not seen.
The rest of the deployment guide, including Compose, the plain binary and the security checklist, is the deployment overview.