Self-hosting

On-premises setup

Run UserTasks fully on your own infrastructure with no SaaS and no outbound internet: PostgreSQL and PgBouncer, built-in auth or Keycloak, MinIO, SSE, the worker, an SMTP relay and environment secrets.

This guide runs UserTasks entirely on your own infrastructure. It uses no SaaS and needs no outbound internet. Every component below can run in an air-gapped network. The on-premises reference profile is the tested version of this setup.

AreaRecommendedAlternatives
DatabasePostgreSQL 15+, behind PgBouncer in transaction modePatroni, CloudNativePG, Crunchy PGO, EDB
AuthBuilt-in (email + password)Keycloak, Authentik, ADFS, Dex or Zitadel over OIDC or SAML (AUTH_SSO_PROVIDERS)
StorageMinIO or another S3-compatible store (Ceph RGW, SeaweedFS, StorageGRID)a local disk or NFS volume (STORAGE_PROVIDER=local)
Realtimesse (LISTEN/NOTIFY; needs no extra service)
Schedulerworker containerOS cron, a systemd timer or a Kubernetes CronJob calling /api/cron/tick (external)
EmailSMTP to Exchange, Postfix or any relay
Secretsenv with Docker or Kubernetes secrets (<VAR>_FILE, SECRETS_FILES_DIR)HashiCorp Vault / OpenBao
KMSenv (ENCRYPTION_KEY)Vault / OpenBao Transit (KMS_PROVIDER=vault_transit) for envelope encryption
CacheValkey or Redis (CACHE_PROVIDER=redis)postgres (no extra service); KeyDB, Dragonfly; memory for a single instance
AI agentsOllama, vLLM, or any OpenAI-compatible endpoint (an agent connector)
LogsSyslog over TLS (rsyslog, syslog-ng, a SIEM), Splunk HEC, an OpenTelemetry Collector, or a webhook to Fluent Bit / Vectorsyslog over TCP / UDP on a private network

1. Images

You need four images: usertasks (built from the repository's Dockerfile), postgres:16, MinIO, and optionally edoburu/pgbouncer and Keycloak.

On a connected machine, build or pull them, then copy them into the isolated network:

bash
docker build -t registry.internal/usertasks:<version> .
docker save registry.internal/usertasks:<version> | gzip > usertasks.tar.gz

Then push them to your internal registry.

The image leaves the Snowflake, Oracle and MongoDB drivers out unless you build it with --build-arg INCLUDE_OPTIONAL_DB_DRIVERS=true, and a missing driver can't be added to the finished image. Choose the drivers before you build: see Optional drivers.

2. Configuration

  1. Start from deploy/compose/standalone.env.example. npm run stack -- init fills in fresh secrets for you.
  2. Set APP_URL to the address users open, for example https://tasks.corp.example.
  3. Point SMTP_HOST / SMTP_PORT at your relay. Use SMTP_STARTTLS=required for a relay that supports TLS.
  4. If the database, the relay or your IdP use a private CA, mount the CA certificate and set NODE_EXTRA_CA_CERTS.
  5. For Keycloak, add an entry like this to AUTH_SSO_PROVIDERS:
text
[{"id":"keycloak","type":"oidc","preset":"keycloak","baseUrl":"https://sso.corp.example","realm":"corp","clientId":"usertasks","clientSecret":"secret://keycloak-client-secret"}]

Register the redirect URI <APP_URL>/auth/sso/keycloak/callback in Keycloak.

3. Start and create the first workspace

bash
docker compose --profile standalone up -d --wait      # add --profile pgbouncer if you use it
docker compose exec -e BOOTSTRAP_PASSWORD=… app node admin.cjs bootstrap --email admin@corp.example --org "Corp"
docker compose exec app node admin.cjs instance-admins add ops@corp.example
docker compose exec app node admin.cjs doctor

Startup runs migrations and creates the storage buckets on every up, in the migrate container. When you run Kubernetes instead of Compose, use the same image and commands: a Job for node admin.cjs db migrate, a Deployment for node server.js, and a Deployment or CronJob for node worker.cjs.

4. Check the setup

npm run stack -- smoke --profile onprem runs the release-gate checks: sign-in, files, SSE, email, scheduler ticks, and no outbound internet. To run the same HTTP checks against your own install, use deploy/compose/smoke/smoke.mjs as described in Deployment profiles.

Moving an existing Supabase install

Follow the order in Switching providers.