# Dokploy: the default branch is canary and the image bakes in .env.production

> Dokploy is a self-hostable PaaS for deploying applications and databases onto a VPS, installed with a single curl command that pipes a remote script into bash. The repository ships five Dockerfiles, two licence files, a canary default branch, and a production image that installs its own Docker client and copies .env.production into the image itself.

**Dokploy/dokploy** — Dokploy is a free, self-hostable PaaS alternative to Vercel, Netlify, and Heroku, deploying apps and databases with Docker Compose, backups, and multi-node scaling.

- Repository: https://github.com/Dokploy/dokploy
- Website: https://dokploy.com/
- Stars: 37,573 · Forks: 3,021
- Language: TypeScript
- License: not declared
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/dokploy-dokploy

## Installation is a remote script piped into bash on a VPS

The entire getting started section is one instruction and one command:

```bash
curl -sSL https://dokploy.com/install.sh | bash
```

It is meant to be run on a VPS, and the alternative offered is a hosted instance at app.dokploy.com for anyone who wants to skip installation. Documentation is at docs.dokploy.com, and there is a video tutorial and a Discord.

The consequence is that the script that takes over a machine is not in the repository, so reviewing it means fetching it first and reading it, which the one line form does not let you do. The README also never states which port the panel listens on or what domain it should sit behind, and those are the first two things you need after the install finishes. Routing is handled by Traefik, which Dokploy integrates with automatically, so the missing piece is a hostname and a certificate rather than a reverse proxy to configure.

## The default branch is canary, not a release branch

The repository's default branch is canary, which means a plain clone lands on the integration branch rather than on a tagged release. The build scripts make that explicit, with a script named docker:build:canary that shells out to a build script with the word canary as its argument.

The tagged releases move quickly. The last three are v0.30.6 on 2026-09-08, v0.30.7 on 2026-09-18, and v0.30.8 on 2026-09-29, and the last push to the repository is dated 2026-09-29. The project is not archived.

The consequence is that the difference between reading the code and running the product is a branch name, and the most convenient way to get the source is the one that tracks unreleased work. Anyone who wants to study Dokploy, fork it, or report a bug against what they can reproduce should check out a tag such as v0.30.8 rather than the default branch, because a bug found on canary may already be fixed in the release you actually run, or may only exist there.

## Two licence files and a terms document share the root

The repository description calls Dokploy an open source alternative to Vercel, Netlify and Heroku, and the README describes it as a free, self-hostable PaaS. The root of the repository holds LICENSE.MD, LICENSE_PROPRIETARY.md, and TERMS_AND_CONDITIONS.md, and the license field on the repository is reported as NOASSERTION rather than a single SPDX identifier.

The consequence is that the word open source in the description and the presence of a proprietary licence file are both true statements about the repository, and nothing in the README reconciles them. A self hosted install being free is a statement about the product, while a proprietary licence file is a statement about some part of the code, and the boundary between the two lives in files the README never mentions. Anyone planning to fork, rebrand, or redistribute the panel, or who wants to know what the hosted option at app.dokploy.com is made of, has to read all three documents before the first deployment rather than after.

## The runtime image installs Docker and copies .env.production into itself

The main Dockerfile runs on node:24.4.0-slim with corepack preparing pnpm 10.22.0. The build stage installs a native toolchain including python3, g++, pkg-config, and libsecret-1-dev, then installs from a frozen lockfile with a pnpm store cache. The runtime stage installs tini, curl, unzip, zip, apache2-utils, iproute2, rsync, and git-lfs, then fetches two installers over the network: the Docker installer run with a pinned version of 28.5.2, and the rclone installer. A build argument pins Nixpacks at 1.41.0, which is what builds user applications.

One line deserves attention on its own:

```
COPY .env.production ./.env
```

The consequence is that whatever is in .env.production is baked into an image layer rather than supplied at run time, so a value that belongs in a secret store ends up in something you can copy, push to a registry, and inspect with docker history. The other consequence is that the image is not a passive application: it carries a Docker client, rclone, git-lfs, and Nixpacks, because deploying to other hosts and databases is the product. Two of those are fetched by piping a vendor script into a shell during the build, so the build depends on those URLs staying reachable and unchanged.

## Node 24.4.0 exactly, and a pnpm range wider than the pinned version

The workspace root is named dokploy, is private, and declares workspaces of apps/* and packages/*. The engine requirements are node ^24.4.0 and pnpm >=9.12.0, while packageManager pins pnpm@10.22.0 and the Dockerfile activates exactly that version through corepack.

The consequence is that the declared pnpm range admits a major version the project does not actually use. A machine with pnpm 9 satisfies the engine field, corepack then activates 10.22.0 anyway, and the mismatch only surfaces as a surprise for anyone reading the engine range as the supported set. The Node floor is the opposite case: 24.4.0 is both the engine minimum and the exact base image tag, and the dev dependency on Node types tracks it, so a contributor on an earlier LTS is outside the range with no flexibility built in.

The lint story has a similar shape. lint-staged runs biome check with the write flag on the pattern *, guarded by flags for unmatched files and unknown file types, so every staged path is offered to the formatter.

## Peer dependencies are declared missing on purpose

The pnpm configuration does two things worth knowing about. An overrides block pins the transitive tree: esbuild 0.20.2, better-call 1.3.7, @better-fetch/fetch 1.3.1, tar in the range above 7.5.19 and below 8.0.0, and protobufjs in the range above 7.5.5 and below 8.0.0. Two tar and protobuf resolutions sit in the resolutions field as well, pinning @types/react to 18.3.5 and @types/react-dom to 18.3.0. Separately, peerDependencyRules.ignoreMissing lists prisma, @prisma/client, @prisma/engines, @electric-sql/pglite, typescript, and drizzle-kit.

The consequence is that the install succeeds whether or not those peers are present, which is convenient in a workspace where several apps are built in different orders and unhelpful when something is genuinely absent, because the failure moves from install time to runtime. It also means the database layer is declared in one app and tolerated everywhere else, so a missing client shows up as a runtime error inside a panel that otherwise installed cleanly.

## Five Dockerfiles because there are five separate surfaces

The root carries Dockerfile, Dockerfile.cloud, Dockerfile.monitoring, Dockerfile.schedule, and Dockerfile.server. Alongside them sit apps/ and packages/ as the pnpm workspaces, a committed openapi.json with a generate:openapi script, GUIDES.md, SECURITY.md, CONTRIBUTING.md, a .claude directory with CLAUDE.md, and a .worktreeinclude file.

The consequence is that one install command brings up more than the panel. The cloud variant is a different image from the self hosted one, and monitoring and scheduling are separate images again, so the number of containers a real install runs is larger than the README's single command suggests. The committed openapi.json is the other thing to watch: it is a tracked artifact of the API surface, so a change to a route is not complete until the file is regenerated, and a diff on that file is often the clearest signal that an endpoint moved.

## Conclusion

Dokploy fits a team that wants a Vercel or Netlify shaped control panel on infrastructure it already rents, with Traefik handling routing and one click templates for common open source apps. It does not fit a team that needs an unambiguous open source boundary, because the repository ships a LICENSE_PROPRIETARY.md beside LICENSE.MD, and it does not fit anyone who wants a predictable release, because the default branch is canary rather than a release branch. Before you run the install script, read it rather than piping it straight to bash, read both licence files, check what ends up in .env.production before building an image, and pin a tagged release instead of tracking the default branch.

## FAQ

### What is Dokploy used for?

It is a free, self-hostable Platform as a Service for deploying and managing applications and databases on your own VPS. It handles Node.js, PHP, Python, Go, and Ruby applications, MySQL, PostgreSQL, MongoDB, MariaDB, libsql, and Redis databases, Docker Compose stacks, Traefik routing, and one click templates such as Plausible, Pocketbase, and Calcom.

### Is Dokploy free?

The README describes Dokploy as free and self-hostable, and the installation is a single curl command against your own VPS. The repository root carries both LICENSE.MD and LICENSE_PROPRIETARY.md along with TERMS_AND_CONDITIONS.md, so the terms of the code itself are not settled by that description. A hosted option is offered separately at app.dokploy.com.

### Is Dokploy a VPS?

No. Dokploy is software you install on a VPS, and the README says to run the install command on a VPS and lists self hosting on your VPS as a feature. The VPS is the machine you provide; Dokploy is the deployment panel that runs on it.

### how to install dokploy

Run curl -sSL https://dokploy.com/install.sh | bash on a VPS. The repository also builds images itself, with separate Dockerfiles for the main panel, the cloud variant, monitoring, scheduling, and the server, and a canary build script referenced from the package scripts.

### how to use dokploy templates

Templates deploy open source applications with a single click from the panel, and the README names Plausible, Pocketbase, and Calcom as examples among others. The feature list also covers Docker Compose for anything not in the template gallery, plus automatic backups to external storage for databases.

## Sources

- [Official documentation](https://dokploy.com/)
- [Official README](https://github.com/Dokploy/dokploy#readme)
- [Project repository](https://github.com/Dokploy/dokploy)
- [Release notes](https://github.com/Dokploy/dokploy/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dokploy-dokploy
