DTPulseOn-prem documentation

DTPulse on-prem

Self-hosted DTPulse — full feature parity with our SaaS at dtpulse.com, running on your own infrastructure. Single Docker stack, license-keyed, phone-home heartbeat to our license server.

Requirements

  • Linux host (any distro that runs Docker)
  • Docker 24+ and Docker Compose v2
  • 4 GB RAM, 20 GB free disk
  • Ports 80 and 443 open from the public internet (Caddy auto-provisions Let's Encrypt TLS certs)
  • A DNS A/AAAA record pointing the chosen domain at this host
  • Outbound HTTPS access to dtpulse.com — required at install time to verify your license key, and ongoing (once an hour) for the heartbeat that confirms the license is still valid

Quick install (10 minutes)

One-line installer:

sh -c "$(curl -fsSL https://dtpulse.com/install)"

The installer will prompt for:

  • License keydtp_op_… token issued by sales. Verified against the license server immediately; revoked, mistyped, or wrong-platform keys re-prompt instead of letting install proceed.
  • Admin email — first admin's login
  • Company name — displayed in UI and emails
  • HTTP port — default 80; you can choose another (e.g. 8080) if 80 is already taken
  • SMTP host / user / password / from — for invites, password reset, license-expiry warnings (skippable, but heavily recommended)

It pulls Docker images, brings up the stack, and prints the one-time admin password directly to the install output between SEED_CREDENTIALS markers. The whole thing takes 2–5 minutes on a fresh VPS.

Prerequisites: a Linux host with Docker 24+ and Docker Compose v2. If Docker isn't installed:

curl -fsSL https://get.docker.com | sh

Advanced installer (with domain + Caddy + automatic TLS) — if you have a domain ready and want HTTPS from the start, contact sales for the production install kit that runs Caddy with Let's Encrypt out of the box. The one-line installer above is HTTP-only on a bare IP.

First-time login

1. Open https://your-domain.com/ (TLS auto-provisioned by Caddy)
2. Sign in with the admin email and the one-time password from the install output
3. The app will prompt you to change the password immediately

Lost the one-time password before signing in? Re-fetch from logs:

cd /opt/dtpulse
docker compose logs app | grep -A4 SEED_CREDENTIALS

(Available until logs roll. Once SMTP is configured, "Forgot password?" on /signin works too.)

Configuring email

The installer asks for SMTP credentials up-front — if you skipped that step, the app still runs but:

  • Invites can't be sent
  • Password-reset is broken (lock-out risk if admin forgets password)
  • Absence-approval emails don't fire
  • License-expiry warnings won't reach you — you'll find out the day the app blocks access, not 14 days before. This is the most critical reason not to skip SMTP

To add SMTP later, edit /opt/dtpulse/.env:

SMTP_HOST=smtp.resend.com        # or your own Postfix, smtp.sendgrid.net, AWS SES SMTP, etc.
SMTP_PORT=465
SMTP_USER=[email protected]
SMTP_PASS=your-password-or-api-key
EMAIL_FROM=DTPulse [email protected]

Then restart:

cd /opt/dtpulse && docker compose restart app

Verify it works: in DTPulse, go to /admin/staff, invite yourself at a different address, and confirm the invite email arrives. If it doesn't, check the app logs:

docker compose logs app | grep -i 'email\|smtp'

SSO setup (optional)

DTPulse supports SAML 2.0 (Okta, Azure AD, Google Workspace, OneLogin). Configure in /admin/integrations after first login. Per-tenant config — no env vars needed.

Changing the domain after install

If you installed against one hostname (e.g. an IP, or dtpulse.staging.example.com) and later want to move to a different one, update NEXTAUTH_URL and reload:

cd /opt/dtpulse
sed -i 's|^NEXTAUTH_URL=.*|NEXTAUTH_URL=https://new-domain.example.com|' .env
sed -i 's|^DOMAIN=.*|DOMAIN=new-domain.example.com|' .env
docker compose up -d app caddy

Why this matters: every email (password reset, verify-email, absence approvals, lifecycle warnings, review invitations) builds links as ${NEXTAUTH_URL}/.... If NEXTAUTH_URL still points at the old hostname, users will get email links to a host that no longer serves the app — and password-reset / verify-email tokens are stored in your local DB, so they can't be opened from anywhere else either. The app logs a warning at startup if NEXTAUTH_URL is empty or points at our SaaS domain:

docker compose logs app | grep '\[on-prem startup\]'

Backups

Two volumes to back up:

  • pgdata — Postgres data (everything: tenants, employees, KB articles, etc.)
  • ./data/ — runtime files (license cache, initial credentials)

Recommended: nightly pg_dump:

docker compose exec db pg_dump -U dtpulse dtpulse | gzip > backups/$(date +%F).sql.gz

Upgrades

cd /opt/dtpulse && docker compose pull && docker compose up -d

This pulls the latest published image, rolls the app + cron containers. Postgres data and configs are preserved. Migrations run automatically; on a migration failure docker-compose keeps the previous container running, so you have a safe rollback.

To pin a specific version instead of :latest, edit .env:

IMAGE_TAG=v1.4.2

License renewal

Your license has an expiry date. The app warns admins by email 14 days before, 7 days before, and on the day of expiry; after 30 more days of grace period, login is suspended. No data is deleted at any point — entering a new license key on the /blocked page (or via email contact to sales) restores access immediately.

Renewal: request a new key from [email protected], then either:

  • Edit LICENSE_KEY in .env and docker compose restart app
  • Or, if already blocked, paste the new key on the /blocked page

Troubleshooting

Installer rejects the license key at the prompt:

  • Couldn't reach the license server — outbound HTTPS to dtpulse.com is blocked from this host. Open egress for dtpulse.com:443 and retry the prompt.
  • Key not recognized — typo, or you used a key issued for a different platform. Double-check the key sales sent you; it must start with dtp_op_.
  • This key was revoked / already bound to another installation — contact sales for a replacement or to rebind for a legitimate VPS migration.

Site doesn't load (no TLS, no response):

  • DNS: confirm domain points to this host:
dig +short your-domain.com
  • Firewall: ports 80 and 443 open:
sudo ufw status
  • Caddy logs:
docker compose logs caddy

/blocked page appears immediately after install:

  • License-server unreachable from this host
  • Test:
curl https://dtpulse.com/api/license/verify
  • Confirm LICENSE_KEY env matches the key sales issued

Initial admin password not shown / scrolled off:

  • Re-fetch from logs:
docker compose logs app | grep -A4 SEED_CREDENTIALS
  • If logs rolled and nothing matches, use "Forgot password?" on /signin (requires SMTP configured).

Database connection failures after host reboot:

  • Postgres started before Docker; restart: unless-stopped should handle this. If not:
docker compose restart

Support