Skip to Content

Portainer Docker Setup: Stop Memorizing CLI Commands and Start Managing Containers Like a Pro

Install Portainer on your Docker host, deploy stacks with a Compose editor, manage volumes and networks from a browser, and put your entire container infrastructure under one dashboard.

Why Portainer Belongs in Your Docker Stack

The Docker CLI is powerful. It's also a mental tax when you're juggling fifteen services across three environments, trying to remember which container a volume is attached to, or debugging a network that was created six months ago by someone who no longer works at the company.

Portainer is a self-hosted web UI for Docker (and Kubernetes, and Swarm) that gives you a full dashboard for containers, images, volumes, networks, and stacks — without replacing the CLI for cases where the CLI is the right tool. It doesn't abstract Docker away; it makes it visible and navigable. You can still drop to the terminal whenever you need to, and everything you do in Portainer is real Docker under the hood.

This guide walks you through the full Portainer Docker setup: installation with Docker Compose, initial configuration, deploying stacks with the built-in Compose editor, managing volumes and networks, putting Portainer behind a reverse proxy with HTTPS, and hardening it for production.

Prerequisites

Before you start, have these ready:

  • A Linux server or VPS — Ubuntu 22.04/24.04 LTS recommended. Portainer is extremely lightweight: 512 MB RAM and 1 CPU core handles most workloads comfortably.
  • Docker Engine 24+ installed — Follow the official Docker installation guide. Avoid the snap package on Ubuntu — it causes compatibility issues with socket paths.
  • Docker Compose v2 — the docker compose plugin (not legacy docker-compose). Verify with docker compose version.
  • Ports 9443 and 8000 open in your firewall — 9443 is the Portainer HTTPS UI; 8000 is the Edge Agent tunnel (optional if you don't use Edge).
  • A domain name (optional for initial setup, required for production HTTPS) — e.g., portainer.yourdomain.com.
  • Basic Docker familiarity — you should know what a container, image, volume, and network are. Portainer makes them easier to manage; it doesn't explain Docker from scratch.

Step 1: Install Portainer CE with Docker Compose

Portainer CE (Community Edition) is free, open source, and covers everything you need for single-node and multi-node Docker management. Install it as a Docker container — fitting.

Quick Start (Single docker run)

The fastest path to a running Portainer instance:

# Create the persistent data volume
docker volume create portainer_data

# Run Portainer CE
docker run -d \
  -p 8000:8000 \
  -p 9443:9443 \
  --name portainer \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:lts

# Access at https://YOUR_SERVER_IP:9443
# (Portainer uses a self-signed cert by default — accept the browser warning)

Production Docker Compose Setup

For a managed, version-controlled deployment that's easy to update and maintain, use Docker Compose:

# docker-compose.yml
services:
  portainer:
    image: portainer/portainer-ce:lts
    container_name: portainer
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    ports:
      - "9443:9443"
      - "8000:8000"   # Remove if not using Edge Agents
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portainer.rule=Host(`portainer.yourdomain.com`)"
      - "traefik.http.routers.portainer.entrypoints=websecure"
      - "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
      - "traefik.http.services.portainer.loadbalancer.server.port=9443"
      - "traefik.http.services.portainer.loadbalancer.server.scheme=https"
    networks:
      - traefik-net
      - portainer_network

volumes:
  portainer_data:
    name: portainer_data

networks:
  traefik-net:
    external: true
  portainer_network:
    driver: bridge
# Deploy the stack
docker compose up -d

# Confirm Portainer is running
docker ps --filter name=portainer

# Follow startup logs
docker logs portainer -f --tail 30

First Login — Do This Immediately

Navigate to https://YOUR_SERVER_IP:9443. You'll see the Portainer setup screen. Create your admin account right now. If you don't create it within the first few minutes, Portainer locks itself down for security and you'll need to restart the container to reset. Set a strong password — the admin account controls everything.

After logging in, Portainer detects the local Docker environment automatically via the mounted socket. Click Get Started to connect to it, and you'll land on the Environments dashboard.

Step 2: Understand the Portainer Interface

The left sidebar organises everything you manage. The key sections to know:

  • Home — All connected environments (Docker hosts, Swarm clusters, Kubernetes clusters). Click an environment to enter its context.
  • Stacks — Deploy and manage Docker Compose applications. This is where you'll spend most of your time.
  • Containers — Full list of all containers on the host. Start, stop, restart, inspect logs, open a console shell — all from here.
  • Images — Pull, tag, push, and remove images. See which images are in use and which are dangling.
  • Networks — Create, inspect, and remove Docker networks. See which containers are connected to each network.
  • Volumes — Create and manage named volumes. Browse volume contents from the UI.
  • Settings — Authentication, users, SSL certificates, registries, and global configuration.

Step 3: Deploy Applications with Stacks

Stacks are how Portainer handles Docker Compose applications. You paste or type a Compose file directly in the browser, Portainer deploys it, and the stack appears in the UI with full lifecycle management — update, redeploy, stop, delete.

Deploy a Stack from the UI

  1. Click Stacks → Add stack
  2. Give the stack a name (lowercase, no spaces — this becomes the Docker Compose project name)
  3. Choose Web editor and paste your Compose YAML
  4. Add any environment variables in the Environment variables section (these map to .env file entries)
  5. Click Deploy the stack

Example: deploying a simple web app stack from the Portainer web editor:

# Paste this in the Portainer Stack web editor
services:
  app:
    image: nginx:alpine
    container_name: my-app
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.my-app.rule=Host(`app.yourdomain.com`)"
      - "traefik.http.routers.my-app.entrypoints=websecure"
      - "traefik.http.routers.my-app.tls.certresolver=letsencrypt"
      - "traefik.http.services.my-app.loadbalancer.server.port=80"
    volumes:
      - app_data:/usr/share/nginx/html
    networks:
      - traefik-net

volumes:
  app_data:

networks:
  traefik-net:
    external: true

# Set environment variables in the Portainer UI below the editor
# rather than hardcoding secrets in the Compose file

Deploy a Stack from a Git Repository

For GitOps-style deployments, Portainer can pull your Compose file directly from a Git repository and redeploy on push. Under Stacks → Add stack, choose Repository instead of Web editor, and point it at your repo URL, branch, and the path to your docker-compose.yml. Enable GitOps updates and configure a webhook — Portainer will redeploy automatically whenever the branch is updated.

This is the cleanest pattern for production: your Compose files live in version control, changes go through code review, and Portainer handles the actual deployment. For a deep dive into GitOps stacks and advanced deployment patterns, see Portainer Docker Setup: Advanced Stack Management, RBAC, Templates, and Multi-Environment Deployments.

Updating and Redeploying Stacks

# To pull latest images and redeploy a stack:
# 1. Go to Stacks → [stack name] → Editor
# 2. Click "Pull and redeploy" — this is equivalent to:
docker compose pull
docker compose up -d --remove-orphans

# To check what changed in a stack after an edit,
# Portainer shows a diff before confirming the redeploy

# To roll back: Portainer CE does not have built-in rollback
# Keep your Compose files in Git so you can redeploy a previous version

Step 4: Manage Volumes and Networks

Two things that cause the most confusion in Docker deployments — because they're invisible in the terminal unless you know to look — are volumes and networks. Portainer makes them visible and manageable.

Volumes

Under Volumes in the sidebar, you can:

  • See all named volumes with their driver, mount point, and which containers are using them
  • Browse the contents of a volume directly in the browser (useful for checking config files or data without exec-ing into the container)
  • Create new volumes with a specific driver or custom options
  • Remove unused volumes safely — Portainer shows you which volumes are attached before you delete
# CLI equivalent of what Portainer shows under Volumes:
docker volume ls
docker volume inspect portainer_data

# See which containers use a volume:
docker ps -a --filter volume=portainer_data

# Remove dangling (unused) volumes — same as Portainer's "Clean up" action:
docker volume prune

# Portainer also surfaces volumes with no container attached,
# making it easy to find and clean orphaned data

Networks

Under Networks, you can inspect every Docker network on the host: which containers are connected, their IP addresses within the network, and the network's driver and subnet. Creating a new shared network for a reverse proxy stack is one click instead of a terminal command. Connecting or disconnecting a running container from a network is drag-and-drop.

# Create the shared Traefik network from CLI (or use Portainer's UI):
docker network create traefik-net

# Inspect what's connected:
docker network inspect traefik-net

# Connect a running container to an additional network:
docker network connect traefik-net my-app

# Disconnect:
docker network disconnect traefik-net my-app

# All of the above operations are available as single-click actions
# in Portainer's Networks → [network name] → Connected containers view

Step 5: Container Operations — Logs, Console, and Inspection

The Containers list in Portainer is where day-to-day operations happen: checking whether something is running, reading logs, exec-ing into a shell, or inspecting the full container config.

Reading Logs

Click any container, then Logs. You get a live log tail with search and filtering. For troubleshooting startup failures or runtime errors, this is significantly faster than juggling terminal windows. You can filter by log level, search for specific strings, and download the full log as a text file.

Container Console (exec shell)

Click Console on any running container to open a browser-based terminal. You can choose the shell (/bin/sh, /bin/bash, cmd) and the user to run as. For quick inspection of config files, running one-off commands, or debugging inside the container — it's the same as docker exec -it container_name /bin/bash, just from a browser tab.

Inspect Container Config

The Inspect view shows the full JSON output equivalent to docker inspect: mounts, environment variables, network settings, resource limits, restart policy, labels. This is the fastest way to verify that a container was deployed with the right config — especially useful when debugging why a container behaves differently from what's in your Compose file.

Resource Usage

Under Stats, each container shows live CPU, memory, network I/O, and disk I/O. For basic capacity monitoring without deploying a full Prometheus/Grafana stack, this is a quick way to spot a container that's eating all your RAM or maxing out your CPU.

Step 6: Production Hardening

A default Portainer install is functional but needs a few changes before it's safe for production.

Put Portainer Behind a Reverse Proxy with Real HTTPS

By default, Portainer uses a self-signed certificate on port 9443. Browsers complain, and self-signed certs don't protect against MITM the way a proper CA-signed certificate does. Put Portainer behind Traefik or Nginx with a Let's Encrypt certificate.

The Docker Compose file in Step 1 already includes Traefik labels. Once your Traefik stack is running with a letsencrypt cert resolver, Portainer will be served at https://portainer.yourdomain.com with a real certificate. Block direct access to port 9443 once Traefik is in front:

# After setting up Traefik, block direct port 9443 access
# so all traffic must go through the reverse proxy
ufw deny 9443
ufw deny 8000   # Block Edge tunnel if not in use

# Only keep ports 80 and 443 open for Traefik:
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 22/tcp
ufw enable
ufw status

Security Hardening Checklist

  • Set a strong admin password — minimum 12 characters, mixed case, numbers, symbols. Portainer is the keys to your entire Docker host; protect the account accordingly.
  • Enable 2FA — Go to Settings → Authentication → Two-factor authentication. Portainer supports TOTP (Authenticator app). Enable it on the admin account immediately after first login.
  • Never expose the Docker socket publicly — The /var/run/docker.sock mount gives Portainer (and anything inside that container) root-equivalent access to the host. Portainer must run on a trusted server, behind authentication, over HTTPS.
  • Create limited user accounts for your team — In CE, you get three roles: Administrator, Standard User, and Read-Only. Assign the minimum role needed. Don't share the admin account. For fine-grained per-environment access control, see Portainer Business Edition.
  • Disable unused endpoints — If you're not using Edge Agents, remove port 8000 from your Compose file and firewall rules.
  • Keep Portainer updated — Portainer releases security fixes regularly. The :lts tag tracks the current long-term support release. Update by pulling the new image and redeploying the stack.
  • Back up the data volume — All Portainer config (stacks, users, environments, custom templates, settings) lives in portainer_data. Back it up regularly with docker run --rm -v portainer_data:/data -v $(pwd):/backup alpine tar czf /backup/portainer-backup.tar.gz /data.

Updating Portainer

# Pull the latest LTS image
docker compose pull portainer

# Recreate the container with the new image
docker compose up -d portainer

# Confirm the new version is running
docker exec portainer cat /var/lib/dpkg/info/portainer.list 2>/dev/null || \
  docker exec portainer sh -c 'cat /package.json' 2>/dev/null

# Or just check the Portainer UI — version is displayed
# in the bottom-left corner of the dashboard

Tips and Troubleshooting

"Connection failed" when connecting to the local Docker environment

The most common cause is the Docker socket not being mounted correctly, or the user running Docker not having socket access. Verify:

# Check that the socket exists and is accessible
ls -la /var/run/docker.sock
# Should show: srw-rw---- 1 root docker ...

# Verify the Portainer container has the socket mounted
docker inspect portainer | grep -A5 'Mounts'

# Test Docker API access from inside the container
docker exec portainer curl -s --unix-socket /var/run/docker.sock \
  http://localhost/v1.40/info | grep -i 'ServerVersion'

# If you installed Docker via snap on Ubuntu, the socket path may differ
# Reinstall Docker via apt using the official Docker repo instead

Portainer UI says "Initialisation required" / locked out

This happens when the container starts but no admin account is created within the 5-minute initialisation window. Portainer locks itself down as a security measure. Fix it by restarting the container — the timer resets:

# Reset the initialisation timer
docker restart portainer

# Then immediately navigate to https://YOUR_SERVER_IP:9443
# and create the admin account before the 5-minute window closes

# Alternatively, pass --admin-password-file on startup
# to set the admin password non-interactively:
echo "yourpassword" > /tmp/admin_password
docker run -d \
  -p 9443:9443 \
  --name portainer \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:lts \
  --admin-password-file /tmp/admin_password

Stack deploys but containers keep restarting

Click the container in the Containers list, then Logs. The exit reason is almost always in the last few lines. Common causes: missing environment variable, port already in use, volume mount path doesn't exist, or the image's entrypoint script fails. Check logs before assuming the Compose file or Portainer is at fault.

Stack update doesn't pull new image tags

If your Compose file uses a fixed tag (e.g., image: myapp:1.2.3), clicking "Update the stack" won't pull a new image — there's nothing to pull, that tag is already cached. Use the Pull and redeploy button explicitly, which runs docker compose pull first. For floating tags like :latest, always use Pull and redeploy to ensure you get the latest version.

Portainer shows wrong IP for containers

Portainer reads IP addresses from the Docker API. If a container is connected to multiple networks, the displayed IP depends on which network Portainer is reading. Use the Inspect view on the container to see all network attachments and their individual IPs.

Volume browser shows "Permission denied"

Portainer browses volume contents by mounting them into a helper container. If the volume data is owned by a user other than root, the browser may not have read access. The volume data itself is fine — this is a display limitation of the Portainer volume browser, not a data problem.

CE vs Business Edition — What do you actually need?

Portainer CE handles everything a single developer or small team needs: full Docker management, stacks, multi-node support via the Agent, basic user roles, and custom app templates. Business Edition adds fine-grained RBAC (environment-scoped and namespace-scoped access), audit logs, advanced GitOps, Edge compute capabilities, and dedicated support. If you're managing Docker for a team where different members should have different access levels per environment, CE's three-role model will start feeling limiting. For the full breakdown of what's unlocked in BE and when to make the switch, see our deep-dive post: Portainer Docker Setup: API Automation, Edge Deployments, Security Hardening, and Container Logging at Scale.

What to Do Next

You now have a self-hosted Docker management UI that covers your whole container infrastructure. Here's where to go from here:

  • Migrate your existing docker run commands to Stacks — Any container you started with docker run can be represented as a Compose file in Portainer. Do this gradually: create the stack, verify the container behaves identically, remove the old one. You'll end up with every service as a version-controllable Compose definition.
  • Connect remote Docker hosts — Install the Portainer Agent on any additional Docker host and connect it to your Portainer server. All hosts appear in one dashboard under different environments. See the definitive guide: Stop Using the CLI for Everything: The Definitive Portainer Docker Setup Guide.
  • Set up App Templates — Portainer ships with built-in templates for common apps (Nginx, WordPress, Nextcloud, etc.). Add your own custom templates under Settings → App Templates for one-click deployment of your organisation's standard services.
  • Enable Webhooks for GitOps — Connect your Stacks to Git repos and trigger redeployment on push. This turns Portainer into a lightweight CD pipeline without Kubernetes overhead.
  • Set up registry access — Under Settings → Registries, add your private Docker registry credentials. Images from private registries become available in the Stack editor and container pull flow.
  • Explore multi-environment RBAC — Once you have multiple team members or multiple Docker hosts, the question of who can deploy what where becomes important. For everything around access control, audit logs, and scaling Portainer to a team, see: Portainer Docker Setup: Advanced Stack Management, RBAC, Templates, and Multi-Environment Deployments.

Need Help Running Docker Infrastructure for a Team?

Portainer on a single VPS is one thing. Running it across a fleet of Docker hosts with team-based access control, audit logging, GitOps pipelines, and zero-downtime updates is a different project. If your team needs help designing and operating production container infrastructure — from first deployment to multi-environment governance — Sysbrix can help.

Talk to our team → We help engineering teams build reliable, well-managed Docker infrastructure on their own servers — with the visibility and control that SaaS tools charge a premium for.

Uptime Kuma Setup: The Self-Hosted Monitoring Stack You'll Actually Keep Running
Deploy Uptime Kuma with Docker Compose, configure monitors for HTTP, TCP, DNS, and push endpoints, set up real-time alerts, and build a public status page — all on your own server.