Getting Started

Deploy Open Help Desk with Docker Compose in under five minutes.

Open Help Desk is distributed as a single Go binary with the React frontend embedded — there is nothing to install or serve beyond the binary and a PostgreSQL database. The Docker Compose stack in docker/ sets both up with a single command.

Architecture overview

Quick start

Requirements: Docker Engine 24+ and Docker Compose V2.

1

Clone the repository

git clone https://github.com/PubliciaLLC/open-help-desk
cd open-help-desk/docker
2

Create your environment file

cp .env.example .env

Open .env and set the three required values. Generate secure secrets with openssl rand -base64 32.

BASE_URL=http://localhost:8080
SESSION_SECRET=<at-least-32-random-chars>
JWT_SECRET=<different-32-random-chars>
3

Start the stack

docker compose up -d

Docker pulls the images, builds the application (Go + React), and starts PostgreSQL. Migrations run automatically before the app begins serving traffic. Wait for:

open-help-desk  | open-help-desk listening on :8080
4

Open the app and complete setup

Navigate to http://localhost:8080. On a fresh database the app redirects to /setup, where you enter a name, email address, and password to create the first admin account. The setup page is permanently disabled once any user exists — it cannot be reached again.

Data persistence — Postgres data is stored in docker/data/postgres/ and file attachments in docker/data/attachments/. Both directories are gitignored and survive container restarts and rebuilds.

Verify the installation

curl http://localhost:8080/health
# → {"status":"ok"}

Configuration reference

All configuration is read from environment variables at startup. Variables marked required cause the server to exit with an error if unset or empty.

Required

VariableTypeDescription
DATABASE_URLstringFull PostgreSQL DSN. Example: postgres://user:pass@host:5432/db?sslmode=disable
BASE_URLstringPublic-facing URL of the app, no trailing slash. Used in email notification links and SAML SP metadata. Example: https://helpdesk.example.com
SESSION_SECRETstringSecret used to sign and verify session cookies. Must be at least 32 characters. Rotating this value invalidates all active user sessions.
JWT_SECRETstringSecret used to sign JWTs for the OAuth2 token endpoint and API bearer tokens. Must be at least 32 characters.

Server

VariableDefaultDescription
HTTP_PORT8080TCP port the HTTP server listens on. Change this when running behind a reverse proxy that handles TLS.
ATTACHMENT_DIR/data/attachmentsFilesystem path where uploaded files are stored. The directory and its parents are created on startup if they don't exist. Must be writable by the application user (ohd in Docker).
APP_ENVproductionSet to development to enable verbose request logging, relaxed CORS, and friendlier error messages in API responses.

Email (SMTP)

Email notifications are silently disabled when SMTP_HOST is not set. No restart is needed to toggle email; the app checks this value at startup.

VariableDefaultDescription
SMTP_HOSTHostname of the SMTP server. Examples: smtp.sendgrid.net, email-smtp.us-east-1.amazonaws.com
SMTP_PORT587SMTP port. 587 for STARTTLS (recommended), 465 for implicit TLS (SMTPS), 25 for plain/relay.
SMTP_USERSMTP username for authentication.
SMTP_PASSWORDSMTP password. Use a secret manager or Docker secret in production — avoid committing this to .env.
SMTP_FROMThe From address for all outbound email. Example: Open Help Desk <helpdesk@example.com>

SAML 2.0

VariableDefaultDescription
SAML_ENABLEDfalseEnable SAML 2.0 SSO. When true, local auth is disabled for non-admin users.
SAML_METADATA_URLURL to your Identity Provider's SAML metadata XML document. Required when SAML_ENABLED=true.
SAML_CERT_FILEContainer path to the SP certificate PEM file. Required when SAML is enabled.
SAML_KEY_FILEContainer path to the SP private key PEM file. Required when SAML is enabled.

Feature flags

VariableDefaultDescription
MFA_ENABLEDfalseEnable TOTP MFA enrollment for all users. Admins can additionally enforce MFA per-role from the settings panel.
SLA_ENABLEDfalseEnable SLA policy tracking. When false, no SLA-related UI or data is shown.
GUEST_SUBMISSION_ENABLEDfalseAllow unauthenticated ticket submission. Guests receive a tracking number and email link for their specific ticket.

First-run setup

After completing the /setup wizard and creating your admin account, work through this checklist before inviting users:

  1. Create your CTI hierarchy — go to Admin → Categories and define at least one Category. Without a Category, no ticket can be created. See the Admin Guide: Categories for a worked example.
  2. Review ticket statuses — the system creates five default statuses (New, In Progress, Pending, Resolved, Closed). If you need intermediate statuses (e.g. "Awaiting Vendor"), add them now under Admin → Statuses. See Admin Guide: Statuses.
  3. Create groups and assign scope — define which staff teams handle which categories. See Admin Guide: Groups & scope.
  4. Create or invite staff accounts — create staff accounts from Admin → Users, or have staff register themselves and then promote their role. See Admin Guide: Users & roles.
  5. Configure email (optional) — set the SMTP environment variables and restart. Preview and customize templates under Admin → Email. See Admin Guide: Email templates.
  6. Configure SLA policies (optional, requires SLA_ENABLED=true) — set response and resolution targets per priority or category under Admin → SLA. See Admin Guide: SLA policies.
  7. Set up SSO (optional) — configure SAML environment variables and restart. See Authentication: SAML 2.0.
  8. Install plugins (optional) — upload or point to WASM plugin files from Admin → Plugins. See Admin Guide: Installing plugins.

Upgrading

cd open-help-desk
git pull
cd docker
docker compose up -d --build

The application runs database migrations automatically on startup, before it begins accepting traffic. There is no manual migration step. If a migration fails (e.g. due to a conflict with manual schema changes), the server exits with an error message — check the container logs.

Back up your database before upgrading in production. Migrations are forward-only; there is no automatic rollback if you need to downgrade.

Zero-downtime upgrades

For production deployments where downtime is unacceptable, the recommended approach is:

  1. Take a database backup.
  2. Pull the new image.
  3. Start a second container with the new image pointing to the same database. Migrations run and complete.
  4. Shift traffic to the new container.
  5. Stop the old container.

This works because migrations are idempotent — running them on an already-migrated database is a no-op.

Authentication →