Why self-host a deployment interface on a VPS
A bare VPS with Docker leaves you with one interface: the command line. That works for one machine, gets tedious for five, and becomes unmanageable for a team. A web interface centralises logs, image updates, environment variables and TLS certificates — without forcing you to maintain a full CI/CD pipeline from day one.
Two families compete: managers (Portainer, Yacht) that expose Docker as-is, and lightweight PaaS (Dokploy, Coolify, CapRover) that add a deployment layer — Git branches, automatic domains, Let's Encrypt certificates. The difference is not cosmetic: it determines whether your developers can deploy on their own or need SSH access.
What you gain by deploying this template on your VPS
- Deploy from Git or Docker Hub in a few clicks, no SSH required
- Automatic TLS certificate management via Let's Encrypt
- Versioned environment variables, isolated per project
- Centralised logs and web terminal from your browser
- Health dashboard: CPU, RAM, container status
- Manage Docker volumes and networks without memorising commands
Portainer in 2026: the end of the Community Edition
Portainer CE (Community Edition) was the reference tool for six years. In 2026, Portainer restructured its licensing: the Community Edition is now limited to Kubernetes or has become paid (Portainer Business). Existing Portainer 2.x instances continue to run, but CE is no longer evolving.
This shift has several practical consequences. New installations looking for a free Docker interface have no clear CE roadmap. The portainer/portainer-ce:latest images still exist and update, but the community channel has lost momentum. And critically — Portainer 2.x has accumulated two CVEs scoring 9.4 that remain active on any unpatched installation.
If you are still running Portainer 2.x, the question is not whether to migrate on principle: it is whether your instance is up to date and whether the risk is acceptable.
Critical Portainer 2.x CVEs: check and patch
Two CVEs affect Portainer versions 2.33.0 to 2.40.x:
CVE-2026-44848 (CVSS 9.4) — The /plugins/* endpoints are not protected by authentication. An unauthenticated attacker can query and modify the Docker plugins on your host via the Portainer API.
CVE-2026-44849 (CVSS 9.4) — The Swarm restrictions mechanism can be bypassed: a Portainer user with reduced rights can perform actions normally reserved for administrators on a Swarm cluster.
Fixed versions: 2.33.8, 2.39.2, 2.41.0.
To check your version:
docker exec portainer /app/portainer --versionTo update:
docker pull portainer/portainer-ce:latest
docker stop portainer
docker rm portainer
docker run -d \
--name=portainer \
--restart=always \
-p 8000:8000 -p 9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latestIf you are below 2.33.8 or 2.39.2 depending on your branch, update before continuing to use the instance. Portainer exposed on the internet without this patch is a direct intrusion vector into your Docker host.
Dokploy CVE: the hardcoded secret (< 0.29.13)
Dokploy is not without vulnerabilities. CVE-2026-45631 (CVSS 10.0) affects versions prior to 0.29.13: an authentication secret was hardcoded in the source code, allowing anyone who knew this secret to authenticate on any Dokploy instance.
This CVE is fixed since version 0.29.13. To check and update:
# Check the installed version
docker exec dokploy dokploy --version
# Update via the official script
curl -sSL https://dokploy.com/install.sh | shDokploy uses BETTER_AUTH_SECRET as a session key. On an installation prior to 0.29.13, this value was static in the code. After updating, verify your Dokploy docker-compose.yml declares a strong random value:
grep BETTER_AUTH_SECRET /etc/dokploy/docker-compose.ymlIf the variable is empty or missing, generate a new value and restart:
export BETTER_AUTH_SECRET=$(openssl rand -hex 32)Prerequisites to host Portainer or Dokploy
Both tools run on Docker. Dokploy requires slightly more resources as it bundles a PostgreSQL database and a Traefik proxy alongside the main service.
Minimum recommended resources
- Portainer 2.x: 1 vCPU, 512 MB RAM, Docker 20+ — runs on the smallest VPS
- Dokploy: 2 vCPU, 2 GB RAM, Docker 24+ — PostgreSQL and Traefik add ~400 MB at startup
- A domain or subdomain pointing to your VPS (Dokploy needs it for automatic certificates)
- Ports 80 and 443 open if you use Let's Encrypt via Traefik (Dokploy)
- Root or sudo SSH access for initial installation
Deploy Dokploy on a VPS (≥ 0.29.13)
Install via the official script
The simplest and most up-to-date method:
curl -sSL https://dokploy.com/install.sh | shThe script installs Docker if needed, generates a
docker-compose.ymlin/etc/dokploy/, creates a randomBETTER_AUTH_SECRETand starts everything.Verify the BETTER_AUTH_SECRET
After installation, confirm the variable is properly generated:
grep BETTER_AUTH_SECRET /etc/dokploy/docker-compose.ymlYou should see a random 64-character string. If the variable is empty, generate one and restart:
sed -i "s/BETTER_AUTH_SECRET=/BETTER_AUTH_SECRET=$(openssl rand -hex 32)/" /etc/dokploy/docker-compose.yml docker compose -f /etc/dokploy/docker-compose.yml up -dAccess the interface and create the admin account
Dokploy listens on port 3000 by default. Open
http://<your-vps-ip>:3000in your browser. The first visit prompts you to create the administrator account. Do this immediately — the interface is public until this account exists.Configure your domain and enable HTTPS
In Dokploy settings → Server → Domain, enter your FQDN (
dokploy.yourdomain.com). Traefik automatically retrieves a Let's Encrypt certificate via ACME HTTP-01. Your DNS must point to the VPS before this step.Update Dokploy
Dokploy exposes an Update button in its settings, or use the script:
curl -sSL https://dokploy.com/install.sh | shThe script is idempotent: it updates the image without overwriting your configuration or projects.
Portainer as an alternative — quick installation
If you prefer to stay on Portainer (existing instance or deliberate choice), the reference command:
docker run -d \
--name=portainer \
--restart=always \
-p 9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latestAccess at https://<ip>:9443. Replace latest with 2.41.0 to pin the version and avoid silent updates.
Portainer 2.x vs Dokploy — full comparison 2026
Scroll the table
| Criterion | Portainer 2.x | Dokploy |
|---|---|---|
| Licence | CE free (Docker only) | Open-source (Apache 2.0) |
| CE future | Limited — CE 3.0 Kubernetes/paid | Active roadmap, growing community |
| Active CVEs | CVE-2026-44848 + 44849 (CVSS 9.4) if < 2.41.0 | CVE-2026-45631 (CVSS 10.0) if < 0.29.13 |
| Minimum RAM | 512 MB | 2 GB (PostgreSQL + Traefik included) |
| Git deployment | No (Docker images only) | Yes (GitHub, GitLab, Gitea, Bitbucket) |
| TLS certificates | Manual (external reverse proxy) | Automatic Let's Encrypt via Traefik |
| App templates | Yes (built-in library) | Yes (imported Docker Compose) |
| Private registry | Yes | Yes |
| Integrated CI/CD | No | Git webhooks + notifications |
| Kubernetes | Yes (paid BE) | No (Docker Compose / Swarm) |
| Multi-server | Yes (endpoints) | Yes (remote agents) |
| Interface | Mature, exhaustive | Modern, project-oriented |
When to stay on Portainer, when to move to Dokploy
Staying on Portainer 2.x makes sense in three situations: you already manage a Docker Swarm or Kubernetes cluster with complex configurations; you have teams that know the interface well with documented runbooks; or you need the level of detail on networks and volumes that Portainer exposes natively.
In these cases, the non-negotiable requirement is being on a patched version (2.33.8, 2.39.2 or 2.41.0) and restricting access to the interface: never expose port 9000/9443 directly on the internet, place Portainer behind a reverse proxy with additional authentication if non-admin users access it.
Migrating to Dokploy is the right decision if: you are starting fresh on a new VPS; you want developers to deploy from Git without SSH; you manage multiple domains and do not want to maintain Nginx/Traefik manually; or you are looking for an alternative after the CE 3.0 change for your Docker use case.
Migration from Portainer to Dokploy: concrete steps
Export your Portainer inventory
Before migrating, list your active stacks and containers:
docker ps --format 'table {{.Names}} {{.Image}} {{.Status}}' docker volume ls docker network ls --filter driver=bridgeFor each Portainer stack, retrieve the
docker-compose.ymlfrom the interface (Stacks → your stack → Editor) or from the filesystem.Back up data volumes
Named volumes contain your application data. Back them up before any operation:
docker run --rm \ -v <volume_name>:/data \ -v $(pwd):/backup \ alpine tar czf /backup/<volume_name>.tar.gz /dataRepeat for each critical volume.
Install Dokploy on the same VPS or a dedicated VPS
If migrating on the same host, stop Portainer first to free the ports:
docker stop portainer && docker rm portainerThen run the Dokploy installation (see procedure above). Dokploy uses Traefik on ports 80/443 — verify no other service occupies them.
Recreate your projects in Dokploy
In Dokploy, create a Compose Project for each Portainer stack:
1. Projects → New project
2. Add a Docker Compose type service
3. Paste yourdocker-compose.ymlor point to your Git repository
4. Configure environment variables in the Environment tab
5. Assign a domain — Dokploy configures Traefik automaticallyVerify and switch DNS
Deploy first on a test subdomain (
test.yourdomain.com). Once validated, update your DNS entries to point to Dokploy. Traefik issues certificates automatically once DNS propagates.
Troubleshooting: common errors
Portainer — interface unreachable after update
Verify that the portainer_data volume is properly mounted. An update without a named volume starts from an empty base and prompts for admin account creation again.
Portainer — TLS handshake error on port 9443
Portainer generates a self-signed certificate. Your browser warns you correctly. Place Portainer behind a reverse proxy (nginx, Caddy) with a real certificate for secure production access.
Dokploy — Traefik not generating a certificate
Most common cause: DNS has not yet propagated to the VPS. Check with dig +short your-domain.com. Traefik waits for a valid DNS response before starting the ACME challenge. If DNS is correct, verify port 80 is open (ufw allow 80).
Dokploy — Git deployment failing silently
Check the service logs in the interface (Deployments → Logs tab). The most common error is a missing BETTER_AUTH_SECRET or an SSH key not configured for private repositories. Dokploy offers a Generate SSH Key button in the project settings for private GitLab/GitHub repositories.
The two tools are not always mutually exclusive
Portainer can manage multiple remote Docker endpoints from a single interface — including a VPS where Dokploy is installed. Some teams keep Portainer as an observation tool and Dokploy as a deployment tool. This is not an antipattern if roles are clearly separated. The cost: two surfaces to patch. If you have chosen this architecture, make sure both tools are up to date — see the CVEs at the start of the article.
To go further: alternatives to Portainer CE 3.0, complete Dokploy deployment guide and the critical Dokploy CVE 2026 update.