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 key —
dtp_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) if80is 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_KEYin.envanddocker compose restart app - Or, if already blocked, paste the new key on the
/blockedpage
Troubleshooting
Installer rejects the license key at the prompt:
Couldn't reach the license server— outbound HTTPS todtpulse.comis blocked from this host. Open egress fordtpulse.com:443and 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 withdtp_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_KEYenv 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-stoppedshould handle this. If not:
docker compose restart
Support
- Sales / license renewal:
[email protected] - Security disclosures:
[email protected]