|
| 1 | +# Dokploy on Terrarium |
| 2 | + |
| 3 | +Dokploy is useful when you want a UI-driven deployment control plane for many Docker apps, not just one hand-managed Compose stack. |
| 4 | + |
| 5 | +On Terrarium, the clean model is: |
| 6 | + |
| 7 | +- one LXC container runs the Dokploy UI |
| 8 | +- one or more other LXC containers act as Dokploy deployment servers |
| 9 | +- each deployment server has its own Docker daemon, volumes, images, and app runtime state |
| 10 | +- Terrarium publishes the Dokploy UI and any app domains through the host Traefik |
| 11 | + |
| 12 | +That means Dokploy gets the server model it expects, while Terrarium still gives you LXD isolation and ZFS snapshots around each Docker host. |
| 13 | + |
| 14 | +## When to use this instead of plain Compose |
| 15 | + |
| 16 | +Use [Isolated Docker Compose deployments](./compose) when you want one app stack in one container and you are comfortable managing Compose files yourself. |
| 17 | + |
| 18 | +Use Dokploy when you want: |
| 19 | + |
| 20 | +- a browser UI for many apps and services |
| 21 | +- project-level deployment history, logs, environment variables, and redeploy buttons |
| 22 | +- remote deployment servers that can be added over SSH |
| 23 | +- multiple Docker hosts, where each Terrarium LXC can be treated as a separate Dokploy server |
| 24 | + |
| 25 | +This is a good fit for “many small self-hosted apps” or “several product stacks with different blast radii”. |
| 26 | + |
| 27 | +## Important architecture note |
| 28 | + |
| 29 | +Dokploy normally assumes it owns ports `80`, `443`, and `3000` on the server where it is installed. |
| 30 | + |
| 31 | +Inside Terrarium, that is fine because those ports are inside the `dokploy` LXC, not on the host. The Terrarium host still owns public `80` and `443`. |
| 32 | + |
| 33 | +The practical routing model is: |
| 34 | + |
| 35 | +- Dokploy UI listens on `dokploy:3000` |
| 36 | +- Terrarium publishes `https://dokploy.example.com` to `dokploy:3000` |
| 37 | +- each Dokploy deployment server LXC runs its own internal Traefik |
| 38 | +- Terrarium publishes each app hostname to that deployment-server LXC on port `80` |
| 39 | + |
| 40 | +So you get two proxy layers for app traffic: |
| 41 | + |
| 42 | +```text |
| 43 | +internet -> Terrarium Traefik -> app-server LXC Traefik -> app container |
| 44 | +``` |
| 45 | + |
| 46 | +That sounds like a lot, but it keeps responsibilities clean. Terrarium handles the public edge and host firewall; Dokploy handles per-app routing inside the Docker host it manages. |
| 47 | + |
| 48 | +## Create the Dokploy UI container |
| 49 | + |
| 50 | +You can create the container from the LXD UI or from the host CLI: |
| 51 | + |
| 52 | +```bash |
| 53 | +lxc launch images:ubuntu/24.04 dokploy |
| 54 | +``` |
| 55 | + |
| 56 | +Install Dokploy inside it: |
| 57 | + |
| 58 | +```bash |
| 59 | +lxc exec dokploy -- bash |
| 60 | +apt-get update |
| 61 | +apt-get install -y curl |
| 62 | +curl -sSL https://dokploy.com/install.sh | sh |
| 63 | +exit |
| 64 | +``` |
| 65 | + |
| 66 | +Publish the UI through Terrarium: |
| 67 | + |
| 68 | +```bash |
| 69 | +lxc config set dokploy user.proxy "https://dokploy.example.com:3000@auth:admins" |
| 70 | +terrariumctl proxy sync |
| 71 | +``` |
| 72 | + |
| 73 | +The `@auth:admins` layer is optional but recommended. Dokploy still has its own login, but this keeps the panel behind Terrarium SSO before the Dokploy login page is even reached. |
| 74 | + |
| 75 | +Open `https://dokploy.example.com` and create the initial Dokploy admin account. |
| 76 | + |
| 77 | +## Create a deployment-server LXC |
| 78 | + |
| 79 | +Each deployment server is where your apps actually run. Start with one: |
| 80 | + |
| 81 | +```bash |
| 82 | +lxc launch images:ubuntu/24.04 apps-a |
| 83 | +``` |
| 84 | + |
| 85 | +Prepare SSH access inside that container. Dokploy’s remote server setup expects SSH and bash. |
| 86 | + |
| 87 | +```bash |
| 88 | +lxc exec apps-a -- bash |
| 89 | +apt-get update |
| 90 | +apt-get install -y bash curl openssh-server |
| 91 | +systemctl enable --now ssh |
| 92 | +mkdir -p /root/.ssh |
| 93 | +chmod 700 /root/.ssh |
| 94 | +sed -i 's/^#\?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config |
| 95 | +sed -i 's/^#\?PermitRootLogin .*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config |
| 96 | +systemctl restart ssh |
| 97 | +exit |
| 98 | +``` |
| 99 | + |
| 100 | +In the Dokploy UI: |
| 101 | + |
| 102 | +1. Open Settings → SSH Keys. |
| 103 | +2. Create an SSH key for remote servers. |
| 104 | +3. Copy its public key. |
| 105 | + |
| 106 | +Add that public key to the deployment-server container: |
| 107 | + |
| 108 | +```bash |
| 109 | +lxc exec apps-a -- bash -lc 'cat >> /root/.ssh/authorized_keys' |
| 110 | +``` |
| 111 | + |
| 112 | +Paste the public key, press Enter, then press `Ctrl-D`. |
| 113 | + |
| 114 | +Find the private LXD address for the deployment server: |
| 115 | + |
| 116 | +```bash |
| 117 | +lxc list apps-a -c n4 |
| 118 | +``` |
| 119 | + |
| 120 | +In Dokploy: |
| 121 | + |
| 122 | +1. Open Remote Servers. |
| 123 | +2. Add a Deployment Server. |
| 124 | +3. Use the `apps-a` IPv4 address, user `root`, and the SSH key you created. |
| 125 | +4. Use Enter Terminal to confirm connectivity. |
| 126 | +5. Run Setup Server from the Deployments tab. |
| 127 | +6. Wait until Dokploy validates Docker, Swarm, the Dokploy network, and its app directory. |
| 128 | + |
| 129 | +Repeat this pattern for `apps-b`, `apps-c`, or any other deployment boundary you want. |
| 130 | + |
| 131 | +## Deploy an app |
| 132 | + |
| 133 | +In Dokploy, create a Project, then create a Docker Compose service. |
| 134 | + |
| 135 | +For most Compose services: |
| 136 | + |
| 137 | +1. Use Dokploy’s Docker Compose mode, not Stack mode, unless you deliberately want Swarm semantics. |
| 138 | +2. Put secrets in Dokploy environment variables. |
| 139 | +3. In the Compose file, load them with `env_file: .env` or reference the specific variables with `${VAR}`. |
| 140 | +4. Prefer Dokploy’s Domains tab over hand-written Traefik labels. |
| 141 | +5. Use `expose`, not public `ports`, for app services that should only be reachable through Dokploy’s Traefik. |
| 142 | + |
| 143 | +For persistent data, Dokploy documents two patterns: |
| 144 | + |
| 145 | +- `../files/...` bind mounts when you want direct file access on the deployment server |
| 146 | +- Docker named volumes when you want Dokploy’s volume backup features |
| 147 | + |
| 148 | +For databases and important app state, named volumes are usually the better default. |
| 149 | + |
| 150 | +## Publish app domains through Terrarium |
| 151 | + |
| 152 | +Dokploy can configure the route inside the deployment-server LXC, but the public internet still reaches the Terrarium host first. |
| 153 | + |
| 154 | +If `apps-a` hosts: |
| 155 | + |
| 156 | +- `https://whoami.example.com` |
| 157 | +- `https://notes.example.com` |
| 158 | + |
| 159 | +then expose those hostnames from the Terrarium host to the `apps-a` container’s internal Traefik on port `80`: |
| 160 | + |
| 161 | +```bash |
| 162 | +lxc config set apps-a user.proxy "https://whoami.example.com:80,https://notes.example.com:80" |
| 163 | +terrariumctl proxy sync |
| 164 | +``` |
| 165 | + |
| 166 | +In Dokploy, configure the same domains on the Compose services using the Domains tab. Dokploy will route by `Host` header inside `apps-a`; Terrarium will terminate public TLS and forward HTTP to `apps-a:80`. |
| 167 | + |
| 168 | +When you add another app domain to that deployment server, append it to the same comma-separated `user.proxy` label and run `terrariumctl proxy sync` again. |
| 169 | + |
| 170 | +## Recommended deployment boundaries |
| 171 | + |
| 172 | +Treat each LXC as a Dokploy server with a clear purpose: |
| 173 | + |
| 174 | +- `apps-public`: low-risk public websites |
| 175 | +- `apps-internal`: internal tools, route-protected by Terrarium when possible |
| 176 | +- `apps-labs`: experiments and disposable stacks |
| 177 | +- `apps-client-a`: one client or project with separate snapshots and rollback |
| 178 | + |
| 179 | +This is the nice Terrarium/Dokploy combination: Dokploy gives you the app deployment UI, and Terrarium gives you server-shaped isolation without buying a separate VPS for each boundary. |
| 180 | + |
| 181 | +## Snapshots and rollback |
| 182 | + |
| 183 | +Before major app migrations or Dokploy server changes, snapshot the deployment-server LXC: |
| 184 | + |
| 185 | +```bash |
| 186 | +lxc snapshot apps-a before-big-upgrade |
| 187 | +``` |
| 188 | + |
| 189 | +If a deploy damages the Docker host badly enough that normal rollback is painful, restore the LXC snapshot instead of rebuilding the whole VPS. |
| 190 | + |
| 191 | +Dokploy’s own volume backups still matter for application-level recovery. Use Terrarium snapshots for infrastructure rollback and Dokploy/S3 backups for app data portability. |
| 192 | + |
| 193 | +## Security notes |
| 194 | + |
| 195 | +- Keep remote server SSH on the private LXD network; do not publish SSH from app-server containers. |
| 196 | +- Use key-based SSH only. |
| 197 | +- Do not expose Docker ports with public `ports:` unless you intentionally want them reachable through the deployment-server network path. |
| 198 | +- Remember that Docker can bypass UFW on a normal host; inside this model, Docker is inside the LXC and Terrarium’s host firewall remains the public edge. |
| 199 | +- Use Terrarium route auth for the Dokploy UI, and use Dokploy’s own auth and permissions inside the panel. |
| 200 | + |
| 201 | +## Upstream docs used for this guide |
| 202 | + |
| 203 | +- [Dokploy installation](https://docs.dokploy.com/docs/core/installation) |
| 204 | +- [Dokploy remote servers](https://docs.dokploy.com/docs/core/remote-servers) |
| 205 | +- [Dokploy deploy server instructions](https://docs.dokploy.com/docs/core/remote-servers/instructions) |
| 206 | +- [Dokploy remote server validation](https://docs.dokploy.com/docs/core/remote-servers/validate) |
| 207 | +- [Dokploy Docker Compose](https://docs.dokploy.com/docs/core/docker-compose) |
| 208 | +- [Dokploy Docker Compose domains](https://docs.dokploy.com/docs/core/docker-compose/domains) |
| 209 | +- [Dokploy remote server security](https://docs.dokploy.com/docs/core/remote-servers/security) |
0 commit comments