Reading time: ~18 minutes Audience: Homelabbers who want a web UI to manage Docker containers


What Is Portainer?

Overview

Portainer is a lightweight web UI for managing Docker, Docker Swarm, Kubernetes, and Nomad environments. It turns complex CLI commands — docker run, docker network create, docker volume inspect — into clickable buttons. For homelabbers, it is the fastest way to deploy, update, and monitor containers without memorizing flags.

Portainer CE (Community Edition) is free and open-source. It supports up to 5 nodes in the free version, which is more than enough for most homelabs.

Key Benefits

  • Stack deployment: Paste a docker-compose.yml into the UI and deploy it in one click.
  • Container logs: Stream logs in real-time from the browser.
  • Image management: Pull, inspect, and delete images without touching the CLI.
  • Volume browser: Browse files inside named volumes — useful for debugging config files.
  • User management: Create multiple users with role-based access control (RBAC).
  • App templates: One-click deploy popular apps (NGINX, MySQL, Redis, WordPress).

Prerequisites

Hardware Requirements

  • A Linux server (x86_64 or ARM64) with Docker installed.
  • Minimum 2 GB RAM (4 GB recommended if running multiple stacks).
  • 20 GB free storage for images and volumes.

Software Requirements

  • Docker Engine 20.10+ and Docker Compose plugin.
  • A user with docker group membership (or root access).
  • Optional: A domain name and DNS A record pointing to your server.

Knowledge Prerequisites

  • Basic familiarity with Docker concepts (images, containers, volumes, networks).
  • Comfortable editing YAML files.
  • Understanding of reverse proxies (Traefik or Nginx Proxy Manager) for HTTPS.
Component Minimum Recommended
CPU 2 cores 4 cores
RAM 2 GB 4 GB
Storage 20 GB 50 GB+
Network 1 GbE 2.5 GbE

Step 1: Install Docker and Docker Compose

Objective

Prepare the host with Docker Engine and the Compose plugin. Ensure the Docker daemon is running and the user has correct permissions.

Step-by-Step Instructions

# Update system
sudo apt update && sudo apt upgrade -y

# Install prerequisites
sudo apt install -y ca-certificates curl gnupg lsb-release

# Add Docker's official GPG key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Add the repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker

# Verify
docker --version
docker compose version

Step 2: Deploy Portainer CE

Objective

Run Portainer as a Docker container with persistent data storage and restart policies.

Step-by-Step Instructions

# Create a volume for Portainer data
docker volume create portainer_data

# Run Portainer
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:latest

Access Portainer at https://your-server-ip:9443. Create the initial admin account.

Optional: Docker Compose for Portainer

version: "3.8"

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: always
    ports:
      - "8000:8000"
      - "9443:9443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data

volumes:
  portainer_data:

Deploy:

docker compose up -d

Step 3: Deploy a Reverse Proxy Stack

Objective

Set up Traefik as a reverse proxy so all services get HTTPS certificates and domain-based routing.

Step-by-Step Instructions

Create a project directory:

mkdir -p ~/homelab/traefik && cd ~/homelab/traefik

Create docker-compose.yml:

version: "3.8"

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: always
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=admin@yourdomain.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - proxy

  whoami:
    image: traefik/whoami
    container_name: whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.yourdomain.com`)"
      - "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
      - "traefik.http.services.whoami.loadbalancer.server.port=80"
    networks:
      - proxy

networks:
  proxy:
    external: false

Deploy via Portainer: 1. In Portainer, go to StacksAdd stack. 2. Name it traefik. 3. Paste the Compose YAML. 4. Click Deploy the stack.

Verify:

curl -I https://whoami.yourdomain.com

Step 4: Deploy a Monitoring Stack

Objective

Add Prometheus, Grafana, and cAdvisor to monitor container metrics and system performance.

Step-by-Step Instructions

Create a new stack in Portainer named monitoring:

version: "3.8"

networks:
  proxy:
    external: true
  monitoring:
    driver: bridge

services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: always
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
      - "--storage.tsdb.path=/prometheus"
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: always
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=admin
    networks:
      - monitoring
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.grafana.rule=Host(`grafana.yourdomain.com`)"
      - "traefik.http.routers.grafana.tls.certresolver=letsencrypt"

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    container_name: cadvisor
    restart: always
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker:/var/lib/docker:ro
      - /dev/disk:/dev/disk:ro
    networks:
      - monitoring

volumes:
  prometheus_data:
  grafana_data:

Create prometheus.yml:

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']

Deploy the stack in Portainer. Access Grafana at https://grafana.yourdomain.com.


Step 5: Add a Database Stack

Objective

Deploy PostgreSQL and Redis as reusable backing services for apps like Nextcloud, Immich, and Authelia.

Step-by-Step Instructions

Create a database stack in Portainer:

version: "3.8"

networks:
  backend:
    driver: bridge

services:
  postgres:
    image: postgres:16-alpine
    container_name: postgres
    restart: always
    environment:
      - POSTGRES_USER=admin
      - POSTGRES_PASSWORD=changeme-strong-password
      - POSTGRES_DB=postgres
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - backend
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    container_name: redis
    restart: always
    networks:
      - backend
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"

volumes:
  postgres_data:
  redis_data:

Security note: Do not expose PostgreSQL and Redis to the internet. Use the backend network for internal communication only. If you must expose ports, restrict them with UFW or iptables.


Pro Tips

Tip 1: Use Portainer’s Environment Variables

Store secrets in Portainer’s Environment Variables section when creating a stack. This keeps passwords out of your Git repository.

Tip 2: Enable Stack GitOps

Portainer can poll a Git repository for docker-compose.yml changes and auto-deploy. This turns your homelab into a GitOps workflow: 1. Push a new Compose file to GitHub. 2. Portainer detects the change and redeploys.

Tip 3: Use Agent Mode for Remote Hosts

If you have multiple homelab servers, install the Portainer Agent on each:

docker run -d \
  -p 9001:9001 \
  --name portainer_agent \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/lib/docker/volumes:/var/lib/docker/volumes \
  portainer/agent:latest

Then add the agent endpoint in your main Portainer UI.

Tip 4: Backup Portainer Data

Portainer’s database is in its named volume. Back it up with:

docker run --rm -v portainer_data:/data -v $(pwd):/backup alpine \
  tar czf /backup/portainer-backup.tar.gz -C /data .

Troubleshooting Common Issues

Problem 1: Portainer Shows “Unable to Retrieve Containers”

Cause: The Docker socket is not mounted or Portainer lacks permissions. Fix:

docker stop portainer
docker rm portainer
docker run -d ... -v /var/run/docker.sock:/var/run/docker.sock ...

Problem 2: Traefik Not Generating Certificates

Cause: Port 80 is not reachable from the internet, or the DNS record is wrong. Fix: Ensure your router forwards port 80 and 443 to the Docker host. Verify DNS with dig yourdomain.com.

Problem 3: Stack Deploy Fails with “Network Not Found”

Cause: The stack references an external network (e.g., proxy) that does not exist. Fix: Create the network first:

docker network create proxy

Or set external: false in the Compose file.


Conclusion

Summary

Portainer transforms Docker from a CLI-only tool into a visual platform. With a single UI, you can manage reverse proxies, monitoring, databases, and dozens of apps. The stack-based deployment model keeps your homelab organized, reproducible, and version-controlled.

Next Steps

  • Add apps: Deploy Nextcloud, Jellyfin, Immich, and Pi-hole as separate stacks.
  • Set up HTTPS: Ensure every public service has a valid Let’s Encrypt certificate via Traefik.
  • Monitor: Use the Grafana stack to track CPU, memory, and container health.
  • Automate: Enable GitOps or Watchtower for automatic image updates.

Affiliate Opportunities

  • Mini PCs: Beelink, Minisforum for running the Docker host.
  • Storage: Samsung 990 Pro NVMe, WD Red SATA SSDs.
  • Networking: TP-Link Omada or UniFi for managed switches.
  • UPS: APC Back-UPS for protecting the Docker host.

Internal Linking Strategy

  • prerequisitesdocker-compose-for-beginners — “learn Docker Compose basics first”
  • step-3immich-reverse-proxy — “configure Traefik for Immich”
  • step-4docker-monitoring-grafana-prometheus — “deep dive into homelab monitoring”
  • step-5nextcloud-self-hosted — “connect Nextcloud to PostgreSQL”
  • conclusionbest-self-hosted-apps-2026 — “apps to deploy in your Portainer stacks”

CTA

  • What’s in your Portainer stack? Share your Compose templates in the comments.
  • Subscribe for Docker Compose recipes, Portainer tips, and homelab automation guides.