How to install Portainer with Docker Compose v2
Table of contents
- Key takeaways
- Prerequisites
- Prepare compose.yaml
- Launch the container
- First access
- Upgrading Portainer
- Exposing Portainer via Traefik (optional)
- What changed since 2.40
- FAQ
- Does Portainer replace the Docker command line?
- Is it safe to expose Portainer directly to the internet on port 9443?
- Can I manage multiple Docker servers from a single Portainer instance?
- Conclusion
- Sources
Tested with Portainer CE 2.45.0 · Docker Compose v2 · verified
Updated: 2026-09-02
You install Portainer with Docker Compose by writing a single compose.yaml file and running two commands. Portainer CE 2.40 STS supports Docker Compose v2 natively, enables HTTPS on port 9443 out of the box, and manages Docker Engine, Swarm, Kubernetes and ACI from one panel. It runs on Ubuntu 24.04 or Debian 13.
Portainer[1] is the reference web UI for managing Docker containers, Docker Compose stacks, and Swarm or Kubernetes clusters from a single panel. It installs with one compose.yaml file and two commands. Compose is the tool that defines and starts a multi-container application from one YAML file.
CE edition 2.45 LTS was released on 27 August 2026 and rolls up the 2.40 to 2.44 STS releases. It keeps native Docker Compose v2 support, HTTPS enabled by default on port 9443, and a unified panel for Docker Engine, Swarm, Kubernetes, and ACI resources. The same guide is available in Spanish.
This guide installs Portainer CE with Docker Compose v2 on a modern Debian/Ubuntu server (Ubuntu 24.04 LTS / Debian 13 Trixie), using a compose.yaml file and the official docker compose plugin. The plugin has no dash, comes bundled with Docker Engine 20.10+, and replaces the older standalone docker-compose binary.
Key takeaways
- Use
compose.yaml(noversion:key) instead of the olddocker-compose.ymlwith an explicit version. - Pin the image (
portainer/portainer-ce:2.45.0) instead of:latestto prevent silent upgrades. - The named volume (
portainer_data) simplifies backups and updates. - The Docker socket is mounted read-only (
:ro): Portainer doesn’t need direct socket write access. - The first-access form expires in 5 minutes: have everything ready before opening the browser.
Prerequisites
Before starting, you need:
- Server with Docker Engine 24+ installed (includes the
docker composeplugin by default). See the official Docker docs for Debian[2] or Ubuntu[3]. sudoaccess or a user in thedockergroup.- Port
9443free for the HTTPS UI and, optionally,8000for the Edge Agent Tunnel (the channel Portainer uses to manage remote agents without opening inbound ports on them).
Prepare compose.yaml
Create the working directory and configuration file:
mkdir -p ~/docker/portainer
cd ~/docker/portainer
nano compose.yaml
Recommended content:
services:
portainer:
image: portainer/portainer-ce:2.45.0
container_name: portainer
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "9443:9443" # UI HTTPS
- "8000:8000" # Edge Agent tunnel (opcional)
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- portainer_data:/data
volumes:
portainer_data:
Differences from older guides worth knowing:
- No
version:key. The Compose Specification has ignored it since 2023; today it’s redundant and triggers warnings in some versions. compose.yamlfilename preferred overdocker-compose.yml(both work, but the new one is the official standard).- Image pinned to
2.45.0, not:latest. Pinning prevents surprises from silent major upgrades when the host runs a pull. - Named volume (
portainer_data) instead of a bind-mount. Managed by Docker directly and easier to back up with tools like restic[4] ordocker cp. - Docker socket read-only (
:ro). Portainer controls everything via API, it doesn’t need direct host socket write access.

Portainer CE admin panel showing the list of running Docker containers
Launch the container
With the compose.yaml file in place, start Portainer in the background:
docker compose up -d
docker compose ps
You’ll see a portainer container running and listening on 0.0.0.0:9443. If the docker compose command doesn’t exist, make sure you’re using Docker Engine 20.10+ with the integrated plugin, not the legacy docker-compose (with a dash, no longer actively maintained).
First access
Open in your browser:
https://<IP-or-domain>:9443
The first time it will ask you to create the admin user with a minimum 12-character password. The form expires 5 minutes after the container starts; if you take longer, restart with docker compose restart and come back.
On 2.45 LTS the first-access screen also asks for a setup token that Portainer prints in its logs at startup. Run docker compose logs portainer and paste the value from the setup_token=… line into the form, or send it in the X-Setup-Token header if you create the admin through the API. It can be disabled by starting Portainer with --no-setup-token.
Because the HTTPS certificate is self-signed on first install, your browser will warn about a “not private” connection. That’s normal: click “Advanced” then “Continue” to proceed. For production, place Portainer behind a reverse proxy such as Traefik (the proxy that routes HTTPS traffic across containers and renews certificates on its own) with Let’s Encrypt (see below).
Upgrading Portainer
Upgrading Portainer is safe because the portainer_data volume persists users, endpoints, and configuration across versions. The process has three steps. Edit compose.yaml and change the image tag, for example from 2.45.0 to the next published tag. Pull the new image, recreate the container, and finally clean up the old image if you want to free disk space:
docker compose pull
docker compose up -d
docker image prune -f
The volume guarantees the upgrade doesn’t lose data. If something goes wrong, edit the tag back to the previous value and run docker compose up -d again. If you’d rather automate this step entirely, see how to install Watchtower, the tool that watches your images and recreates containers when a new version lands.
Exposing Portainer via Traefik (optional)
If you already have Traefik as ingress with the traefik_public network defined, add labels to the service and drop the port mapping. When Traefik terminates TLS for you, the internal port is 9000 (HTTP), not 9443:
services:
portainer:
image: portainer/portainer-ce:2.45.0
restart: unless-stopped
networks: [traefik_public]
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- portainer_data:/data
labels:
- traefik.enable=true
- traefik.http.routers.portainer.rule=Host(`portainer.example.com`)
- traefik.http.routers.portainer.entrypoints=websecure
- traefik.http.routers.portainer.tls.certresolver=letsencrypt
- traefik.http.services.portainer.loadbalancer.server.port=9000
- traefik.http.services.portainer.loadbalancer.server.scheme=http
networks:
traefik_public:
external: true
volumes:
portainer_data:
With this configuration, Traefik manages the TLS certificate with Let’s Encrypt and Portainer is accessible at https://portainer.example.com without non-standard ports. The same pattern applies to any additional service you deploy; see how to install local MCP servers as an example of a complementary service.
What changed since 2.40
This guide was first written and run against Portainer CE 2.40.0 STS.
The compose file above was re-run on 2 September 2026 against 2.45.0 LTS, released on 27 August 2026. Portainer describes that release as rolling up the work from the 2.40 to 2.44 STS releases (release notes[5]). It comes up unchanged, with the UI on 9443 and the HTTP port 9000 behind it. What is different:
- Setup token on first access. A fresh instance prints a
setup_token=…line in its startup logs and the first-access screen asks for it (the API takes it as theX-Setup-Tokenheader); starting with--no-setup-tokendisables it. Checked here on 2.45.0 with the compose file above. - A critical Docker proxy authorization bypass was fixed in 2.45.0 and backported to 2.39.7 LTS the same day. An install still pinned to 2.40.0 predates that fix, which is the strongest reason to move the tag.
- Authentication events now record the real client IP from
X-Forwarded-Forwhen the request arrives through a trusted proxy, which is relevant to the Traefik setup above. - Kubernetes: advanced node drain options with agent failover, and native Portainer APIs for writing secrets, configmaps, deployments and persistent volume claims instead of proxying calls to kube-apiserver.
- Edge Compute settings can be configured during initial setup and via CLI flags, and the Go toolchain moved to 1.26.6, which closes CVE-2026-39821.
- Portainer’s own install docs[6] now point at the floating
portainer/portainer-ce:ltstag; this guide keeps a pinned2.45.0so that upgrading stays a deliberate step.
FAQ
Does Portainer replace the Docker command line?
Not entirely. Portainer covers most of the day-to-day work from the browser: starting, stopping, inspecting logs, deploying stacks, managing volumes and networks. For fine-grained debugging or automated scripts you’ll still reach for docker and docker compose in a terminal. Both read the same socket, so changes made by one show up instantly in the other.
Is it safe to expose Portainer directly to the internet on port 9443?
It works, but it isn’t advisable long term: the first-access form expires in 5 minutes precisely to reduce that risk, and after setup you should add further authentication. The safer route is placing it behind a reverse proxy with valid TLS and, where possible, restricting access by IP or VPN instead of leaving it open to anyone.
Can I manage multiple Docker servers from a single Portainer instance?
Yes. Besides the local environment, Portainer connects to remote endpoints via the Edge Agent or via a direct TLS API. The Edge Agent is a small container installed on each server that opens an outbound tunnel to your central Portainer. From the same panel you administer every connected host, a Swarm cluster, or Kubernetes.
Conclusion
The modern Portainer install with Compose v2 is simpler than guides from three years ago: a 15-line compose.yaml, a pinned version, HTTPS by default, and persistence in a named volume. For multi-node environments, Swarm, or Kubernetes, Portainer adds the same UI on top of the cluster endpoint without changing this guide’s deployment pattern.
What stays constant across versions:
- Pin the image to control when you upgrade.
- Use a named volume, not a bind-mount, for the
/datafolder. - Mount the socket read-only.
- Put a TLS reverse proxy in front for production.
Sources: [1] Official Portainer CE documentation (Docker install)[6], [2] Official Docker Compose documentation[7], [3] Official Traefik documentation[8], [4] restic documentation[4].