Dokploy

Production Hardening Guide

A checklist-style guide to hardening a self-hosted Dokploy deployment — host, Docker, Dokploy, Traefik, and database — written to implement in an afternoon and review as a checklist.

This guide covers the baseline hardening for a production Dokploy instance: the host it runs on, the Docker engine, Dokploy itself, the Traefik reverse proxy, and the databases it manages. It's written to be implemented end-to-end in an afternoon, and to double as a checklist you can walk through directly (see the Final Checklist and Compliance Mapping).

Each ⬜ below is one control to implement and check off; Section 8 collects all of them into a single table.

This is a baseline. Depending on your threat model, regulatory scope, and data sensitivity, you may need additional controls beyond what's listed here.

1. Shared Responsibility

Dokploy secures the application layer — authentication, authorization, TLS automation, secrets referencing, audit trails. You're responsible for the infrastructure it runs on: the host OS, the network perimeter, the database engine's placement, and backup storage security.

LayerDokploy (the product)You (the operator)
Access control2FA, passkeys, RBAC, custom roles, SSO/OIDC/SAML, SCIM (Enterprise)Assigning least-privilege roles, offboarding users promptly
API/CLIToken-based auth scoped to your organization, optional expiration, Access to API/CLI permissionWho gets that permission, setting an expiration on every token, not sharing tokens across integrations
SecretsSecrets Provider integration (Vault, Infisical, AWS SM, Doppler, Azure KV, Scaleway) — values never stored in the Dokploy DB when referenced this wayOperating the external vault, least-privilege vault credentials, not pasting raw secrets into env vars
Reverse proxy / TLSTraefik auto-config, automatic Let's Encrypt certs, HTTP→HTTPS redirect on ApplicationsDNS records, custom Traefik middlewares (HSTS, rate limiting), keeping the Traefik dashboard closed
Docker engineNone — Dokploy is a Docker/Swarm consumer, not a daemon hardenerdaemon.json config, socket exposure, image provenance, host-level container isolation
Host OSAutomated security check (OS, UFW, SSH, Fail2Ban) on servers added via Remote ServersPatching, SSH configuration, firewall rules, non-root operator accounts
Database engineInternal/external credential toggle, connection UINetwork placement (internal-only vs. exposed), engine patching if self-managed
BackupsOrchestrates backup/restore to your S3 destinationBucket security, encryption at rest, retention policy, testing restores
ObservabilityAudit Logs (Enterprise), deployment notificationsLog shipping, SIEM integration, alerting on auth anomalies

2. Host OS

Run this section on the box before (or right after) installing Dokploy — as root or via sudo.

  • ⬜ Ubuntu or Debian LTS — the only OS families Dokploy's automated security check currently targets.
  • unattended-upgrades installed and enabled for automatic security patches.
  • ⬜ SSH: key-based authentication only, root login disabled, password authentication disabled.
  • ⬜ Non-standard SSH port (optional — reduces automated scan noise, not a substitute for the above).
  • ⬜ Fail2Ban (or CrowdSec) installed, enabled, and jailing SSH.
  • ⬜ Firewall allowing only 22 (or your custom SSH port), 80, 443, with ufw-docker installed so those rules actually apply to Docker-published ports. Keep Dokploy's UI port (3000) closed to the public internet — put it behind Traefik on a domain (see Certificates) or behind a VPN (see the Tailscale guide).
  • ⬜ A non-root user for day-to-day operations (log review, manual docker commands), in the docker group. Reserve root for provisioning and the SSH key Dokploy itself uses to manage the host.

Dokploy validates most of this automatically for servers added under Remote Servers: OS, UFW status and default policy, SSH key-based auth, and Fail2Ban — see Remote Servers → Security for the live check.

Commands

Automatic security patches:

sudo apt update && sudo apt install -y unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades
systemctl status unattended-upgrades

SSH — make sure your key is already in ~/.ssh/authorized_keys for the user you'll log in as before you touch this, then edit /etc/ssh/sshd_config:

PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin no

Reload — don't close your current session until a new connection confirms it works:

sudo systemctl reload sshd

For a non-standard port, add Port 2222 (or your choice) to the same file, reload again, and update the firewall rule below to match.

Fail2Ban, jailing SSH:

sudo apt install -y fail2ban
sudo tee /etc/fail2ban/jail.local <<'EOF'
[sshd]
enabled = true
mode = aggressive
bantime = 1h
findtime = 10m
maxretry = 5
EOF
sudo systemctl enable --now fail2ban

Firewall:

sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

UFW alone does not protect Docker-published ports — Docker writes directly to iptables and bypasses UFW's rules. Install ufw-docker so UFW and Docker agree on what's exposed, then see Remote Servers → Security for the full explanation of why this matters.

sudo wget -O /usr/local/bin/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
sudo chmod +x /usr/local/bin/ufw-docker
sudo ufw-docker install
sudo systemctl restart ufw

Non-root operator user, added to the docker group so it doesn't need sudo for every container command:

sudo adduser deploy
sudo usermod -aG sudo,docker deploy

Copy your public key into /home/deploy/.ssh/authorized_keys before you rely on this account to log in.

3. Docker Engine

  • /etc/docker/daemon.json hardened — live-restore, no userland proxy, log rotation (see Commands below).
  • no-new-privileges applied — not a daemon-wide setting, it's set per service.
  • Never expose the Docker socket over TCP (dockerd -H tcp://..., ports 2375/2376) on any host Dokploy manages. Dokploy orchestrates remote servers over SSH, not the Docker remote API — a TCP socket isn't required for it to work, and one left open is unauthenticated root on the host.
  • ⬜ Private registry credentials configured in Dokploy (see Registry) rather than docker login credentials sitting in shell history; use a registry token scoped to pull/push only what's needed.
  • ⬜ Pin image tags in production (avoid :latest) so a redeploy doesn't silently pull an untested image.

Commands

/etc/docker/daemon.json — a sane baseline:

{
  "live-restore": true,
  "userland-proxy": false,
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Restart the daemon to apply it — this briefly stops dockerd (not your containers, since live-restore keeps them running through the restart once it's set):

sudo systemctl restart docker

The log options cap json-file growth — without them, a noisy container can fill the disk.

no-new-privileges, per service, in the Docker Compose file or the app's Advanced settings:

security_opt:
  - no-new-privileges:true

4. Dokploy

  • 2FA or passkeys for every member with panel access — see Account Security. Passkeys are phishing-resistant and preferred for day-to-day sign-in.
  • SSO/OIDC or SAML if you have an IdP (Enterprise) — see SSO. Enterprise lets you force SSO for the organization, disabling password-based sign-in so every member authenticates through your IdP (and inherits whatever MFA policy you enforce there). Combine with SCIM Provisioning to auto-deprovision users the moment they leave your IdP, rather than relying on someone remembering to remove them from Dokploy.
  • Least-privilege roles. Use the built-in Member permissions (see Permissions) or, on Enterprise, Custom Roles scoped per project/environment. Don't hand out Admin by default.
  • Secrets Providers instead of raw values in env vars — see Secrets Providers. Values are fetched at deploy time and never stored in the Dokploy database; your vault stays the source of truth, and rotation there takes effect on the next deploy.
  • API/CLI tokens — restrict the Access to API/CLI permission to who actually needs it. Tokens are scoped to your organization and can be created with an expiration — always set one instead of leaving a token to stand forever, issue a separate token per integration (CI, webhook, script) so you can revoke one without breaking the others, and regenerate immediately on offboarding or suspected exposure.
  • Audit Logs (Enterprise) covering logins, role changes, deployments, and infrastructure changes — see Audit Logs. This is usually the single most-requested control in a security review.

Without SSO, there's no org-wide "require 2FA" toggle today — enablement is per-user, so treat it as a policy you communicate and audit (via Audit Logs, Enterprise). On Enterprise, forcing SSO for the organization is the actual enforcement path: it removes the password fallback entirely, so whatever MFA your IdP requires becomes mandatory for everyone.

5. Traefik & Networking

  • ⬜ TLS is automatic for domains managed through Dokploy's Applications flow — Let's Encrypt via certResolver, with an HTTP router that redirects to HTTPS by default. Docker Compose domains use Traefik labels instead and need the same redirect added explicitly — see Domains and Docker Compose Domains.
  • HSTS, applied via a custom middleware — not automatic today (see Config snippets below).
  • ⬜ Keep the Traefik dashboard off — it isn't exposed by a default Dokploy install, and it should stay that way unless it's behind auth and off the public network.
  • Rate limiting on public-facing services — no UI toggle for this yet, so it also goes through a custom middleware.
  • Isolate networks per project. By default, apps share dokploy-network. For Docker Compose stacks, enable Isolated Deployments so each stack gets its own network and containers can't reach services in unrelated projects by name.
  • ⬜ No Docker socket over TCP anywhere in the cluster (see Section 3) — this is as much a networking control as a Docker one.

Config snippets

Dokploy's Applications flow writes a domain config like the one shown in Domains — a routers + services block per domain, editable from the app's Advanced → Traefik File System tab. Add a headers middleware for HSTS and reference it from the websecure router's middlewares list:

http:
  middlewares:
    secure-headers:
      headers:
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        stsPreload: true
  routers:
    dokploy-app-name-router-websecure-1:
      middlewares:
        - secure-headers
      # ...rest of the router config Dokploy already generated

Same file, a ratelimit middleware:

http:
  middlewares:
    rate-limit:
      rateLimit:
        average: 100
        burst: 50
  routers:
    dokploy-app-name-router-websecure-1:
      middlewares:
        - rate-limit

average is requests/second sustained per source, burst is the short spike allowed above it — tune both to your traffic pattern before relying on them.

6. Database

  • ⬜ Use Internal Credentials for anything an application in the same network needs — never enable External Credentials (a published port) unless there's a real requirement, and if there is, restrict it with a firewall rule or put it behind a VPN rather than leaving it open to the internet. See Database Connection.
  • Backups to an S3 destination — see Database Backups. Dokploy doesn't encrypt the backup archive itself, so enable encryption at rest on the bucket (SSE-S3 or SSE-KMS) and scope the destination credentials to that bucket only.
  • Test restores. A backup you haven't restored isn't a backup — see Restore (or Volume Backups for volumes) and schedule a periodic restore drill. That's the evidence that actually matters, not the backup schedule itself.

7. Observability

  • Audit Logs (Enterprise) as your primary who-did-what trail — see Audit Logs.
  • External log shipping. Dokploy's built-in Monitoring is currently a Cloud-only feature — for self-hosted, ship container and host logs (Docker's log driver, syslog, or a log shipper) to something outside the box you're monitoring, so a compromised host can't erase its own trail.
  • Alert on auth anomalies — repeated Fail2Ban bans, spikes in Traefik 401/403s, failed Dokploy logins. Wire up a notification provider (Slack, email, webhook) for at least deployment failures and server thresholds as a baseline signal.

8. Final Checklist

#ItemSectionStatus
1Ubuntu/Debian LTS with unattended-upgradesHost OS
2SSH: keys only, no root login, no password authHost OS
3Fail2Ban/CrowdSec active on SSHHost OS
4Firewall limited to 22/80/443; port 3000 not publicHost OS
5Non-root operator user with scoped sudoHost OS
6daemon.json hardened (live-restore, log rotation)Docker
7Docker socket never exposed over TCPDocker
8Registry credentials scoped and stored in DokployDocker
92FA or passkeys enabled for every memberDokploy
10SSO/OIDC configured, and forced org-wide if on EnterpriseDokploy
11SCIM auto-deprovisioning (if on SSO)Dokploy
12Roles follow least privilege / Custom Roles in useDokploy
13Secrets referenced via a Secrets Provider, not raw env varsDokploy
14API/CLI tokens issued per-integration, with an expiration setDokploy
15Audit Logs enabledDokploy
16HTTPS enforced with redirect on every domainTraefik
17HSTS middleware applied to public domainsTraefik
18Traefik dashboard not publicly exposedTraefik
19Rate limiting on public-facing servicesTraefik
20Isolated Deployments enabled per project (Compose)Traefik
21Databases use Internal Credentials onlyDatabase
22Backups encrypted at rest in S3Database
23Restore tested within the last quarterDatabase
24Logs shipped to an external systemObservability
25Alerting on auth failures / deployment failuresObservability

9. Compliance Mapping

Illustrative mapping to common SOC 2 Trust Services Criteria and ISO/IEC 27001:2022 Annex A control families — use it as a starting point and align the exact control IDs with your own GRC tooling.

Guide sectionSOC 2 (Trust Services Criteria)ISO/IEC 27001:2022 Annex A
Host OS (patching, SSH, firewall)CC6.1, CC6.6 — logical & physical access, network protectionA.8.8 (vulnerabilities), A.8.20 (network security)
Docker engine hardeningCC6.1, CC6.8 — access control, malicious software preventionA.8.9 (config management), A.8.20
Dokploy: 2FA/SSO/RBACCC6.1, CC6.2, CC6.3 — access provisioning & authenticationA.5.15 (access control), A.8.5 (authentication)
Secrets ProvidersCC6.1, CC6.7 — restricted access, data transmissionA.8.24 (cryptography), A.8.12 (data leakage prevention)
Audit LogsCC7.2 — monitoring for anomaliesA.8.15 (logging), A.5.28 (evidence collection)
Traefik / TLS / HSTSCC6.7 — encryption in transitA.8.24 (cryptography), A.8.26 (application security)
Network isolationCC6.6 — boundary protectionA.8.20, A.8.22 (network segregation)
Database placementCC6.1, CC6.6A.8.20, A.5.10 (acceptable use of assets)
Backups & restore testingA1.2, A1.3 — availability & recoveryA.8.13 (backup), A.5.29 (continuity)
Observability & alertingCC7.2, CC7.3 — monitoring & incident responseA.8.15, A.5.24 (incident management)

Already tracking these in a GRC platform (e.g. Vanta)? Import this table as evidence links against the matching controls rather than re-deriving the mapping — the control IDs above are the standard families, not your tenant-specific ones.

On this page