Choosing a platform

On this page

Office Sentry runs anywhere that can run two containers from one image and give them one disk. These guides start from what you already run: a container manager, a NAS, a Kubernetes cluster or a cloud. Each uses the published image, ghcr.io/jackd99/officesentry, and keeps keys and passwords in the platform's own settings or secret store, never in a file you commit.

New to it and just want it running? A small Linux server with Docker is the simplest and the best tested: Running it on your own server.

Pick a guide #

You run Guide Tested
A Linux server with Docker Running it on your own server Yes: the project's own server runs it
Portainer Portainer The stack file, with Docker Compose; Portainer's screens not yet
Dockge, Komodo, Coolify or Dokploy Other container managers The stack file, with Docker Compose; the managers not yet
Unraid, Synology or TrueNAS Unraid, Synology and TrueNAS The stack file, with Docker Compose; the NAS systems not yet
Kubernetes (AKS, EKS, GKE, k3s and others) Kubernetes Checked against a Kubernetes 1.32 API server and run as a pod in Docker; not yet on a full cluster
Microsoft Azure Microsoft Azure Not yet; the server it builds runs the tested Docker setup
Amazon Web Services Amazon Web Services Not yet; the server it builds runs the tested Docker setup
DigitalOcean, Google Cloud, Linode, Vultr, Oracle Cloud or another VPS Other clouds and VPS hosts Not yet; the server it builds runs the tested Docker setup
A domain on Cloudflare Cloudflare DNS only: yes. Proxied: Caddy's side tested. Tunnel: not yet
Windows Server without Docker Running on Windows without Docker Yes

"Not yet" means the steps follow each platform's documentation but haven't been run end to end. If one doesn't work as written, please open an issue saying where it went wrong, so the guide can be fixed.

What every platform needs #

Whichever guide you follow, these hold. They explain most of the choices in the guides.

  • One image, two containers. The portal (web) is the image's default command and listens on port 8000. The worker runs python -m officesentry worker from the same image and does all collection, scheduled emails and housekeeping. Run exactly one worker, and one portal (it runs several threads itself).
  • One disk, on the same machine as both. Everything is stored in a SQLite database in /data, which both containers open at once. That needs a local disk or block storage (a server's own disk, Azure Disk, Amazon EBS, Google Persistent Disk, a NAS's own volume). Never put /data on a network file share (NFS, SMB, Azure Files, Amazon EFS): SQLite's documentation says the mode Office Sentry uses doesn't work over a network filesystem, and the database can be damaged. This is why the container services listed at the end aren't supported.
  • The keys kept apart from the data. The key that decrypts the stored certificates and passwords is either generated into secrets.json in a second volume, /keys (the default), or given to both containers as two environment variables from a secret store (below). Either way, save a copy somewhere safe, apart from the backups (Where the keys are).
  • HTTPS in front. Sign-in cookies only work over HTTPS. Put a reverse proxy, ingress or tunnel in front of port 8000, and set OFFICESENTRY_TRUSTED_PROXIES to the number of proxies in front (usually 1), so sign-in limits and the activity log see each person's real address (Securing the portal).
  • User 10001. The containers run as user ID 10001 with a read-only root filesystem and a writable /tmp. A volume Docker or Kubernetes creates is made writable for it; a folder on the host must be owned by user ID 10001 (chown 10001 <folder>), or the portal stops at start with PermissionError: [Errno 1] Operation not permitted: '/data'.
  • Size. At least 2 CPUs and 4 GB of memory for the two together, and disk by the number of clients (How much disk). The image is published for amd64 (Intel and AMD) and, from 2.1.0, arm64 (such as Ampere, AWS Graviton and Raspberry Pi 4 or 5).
  • Network. The server makes outbound HTTPS calls to Microsoft (sign-in, Graph and Exchange Online) and to ghcr.io for updates. Nothing from Microsoft ever connects in: only the people using the portal need to reach it, plus a client's administrator returning from Microsoft's consent page (/consent/callback).
  • Health checks. /health answers 200 while the portal runs: use it for a platform's liveness and readiness checks. /health/ready also answers 503 when the worker, the database or the disk has a problem: use it for an uptime monitor (Hear about problems without signing in), never as the portal's liveness check, or the platform restarts a healthy portal when only the worker has stopped. The worker's check is the command python -m officesentry healthcheck worker.
  • The first admin. On first start the portal's log has a line starting No admin account yet with a one-time link. python -m officesentry setup-link, run in the portal's container, prints it again.

Keys from a secret store #

On a platform with a secret store (Kubernetes Secrets, a container manager's secret variables), you can give the keys to both containers instead of keeping them in a volume:

Variable What it is
OFFICESENTRY_SECRET_KEY signs sign-in cookies: any long random text
OFFICESENTRY_ENCRYPTION_KEYS encrypts stored certificates and passwords: a Fernet key (32 random bytes, URL-safe Base64). Several, separated by commas, during a key change; the first encrypts

Make them on any machine with OpenSSL:

openssl rand -base64 48 | tr -d '\n'; echo         # OFFICESENTRY_SECRET_KEY
openssl rand -base64 32 | tr '+/' '-_'              # OFFICESENTRY_ENCRYPTION_KEYS

or with Docker: docker run --rm ghcr.io/jackd99/officesentry:2 python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" for the encryption key.

Set both, on both containers, before the first start, and keep a copy in your password manager: without the encryption key a backup can't be read. When both are set, no secrets.json is written. To change the encryption key later, put the new key first (new,old), restart, run python -m officesentry rotate-keys in the portal's container, then remove the old key and restart again (Rotating keys). An install that already has a secrets.json keeps using it until both variables are set; to move, copy the two values out of it.

Running commands #

Everything in Running it on your own server that starts docker compose exec web works on every platform: run the same python -m officesentry ... command in the portal's container (a container manager's console, docker exec, or kubectl exec). Backups, restoring, the support package and rotating keys all work this way, and each guide says where its console is.

Not supported, and why #

  • Azure Container Apps, Azure App Service, AWS Fargate (ECS), AWS App Runner, Google Cloud Run and other serverless container services. Their only lasting storage is a network file share (or none), and a SQLite database on one can be damaged. Use a small virtual machine on the same cloud (Azure, AWS, Google Cloud) or its Kubernetes service with block storage (Kubernetes).
  • More than one copy. Two workers would collect everything twice and two portals can't share the database across machines. One server copes with hundreds of clients (How fast).
  • Docker Swarm stacks. Swarm ignores the order the two containers start in (the portal upgrades the database before the worker starts) and places them on any node unless pinned. Run the stack on a standalone Docker host instead.