Self-Host Portainer: Manage All Your Docker Containers from One Dashboard in 15 Minutes
Tired of typing endless docker ps and docker logs commands? If you're running more than three containers, you're wasting time in the terminal that could be spent actually building things. Portainer gives you a clean, powerful web UI to manage your entire Docker infrastructure—from a single Raspberry Pi to a fleet of VPS instances.
In this guide, you'll deploy Portainer CE (Community Edition) with a complete docker-compose.yml, secure it properly, and connect it to multiple environments. Total setup time: 15 minutes.
What Problem Does Portainer Solve?
Docker's command-line interface is powerful but becomes unwieldy fast. You find yourself:
- Running
docker-compose logs -fin five different terminal tabs - Manually editing YAML files and restarting stacks blindly
- Struggling to visualize network connections between containers
- Having no easy way to give team members limited container access
Portainer replaces all of this with a web-based management layer. Think of it as "cPanel for Docker"—but actually good, lightweight, and open-source.
Why Self-Host Portainer?
- Zero ongoing cost: CE is free and Apache-licensed
- Multi-environment: Manage local Docker, remote VPS, and Kubernetes from one UI
- RBAC: Built-in user management with role-based access control
- Stack deployment: Deploy full docker-compose stacks from Git repos or raw YAML
- Resource monitoring: See CPU, memory, and network usage per container in real-time
Prerequisites
| VPS/Server | 1 vCPU, 1GB RAM minimum (2GB recommended) |
| OS | Ubuntu 22.04/24.04, Debian 12, or any Linux with Docker |
| Docker | Engine 20.10+ and Docker Compose v2 |
| Ports | 8000 (Edge agent), 9443 (HTTPS UI) — or 9000 for HTTP |
| Domain (optional) | For Traefik/NGINX reverse proxy with SSL |
Step 1: Create the Docker Compose Configuration
Create a dedicated directory and docker-compose.yml:
mkdir -p ~/portainer && cd ~/portainer
cat > docker-compose.yml << 'EOF'
version: "3.8"
services:
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: unless-stopped
ports:
- "8000:8000" # Edge agent port
- "9443:9443" # HTTPS web UI
# - "9000:9000" # Uncomment for HTTP only (not recommended)
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- portainer_data:/data
environment:
- TZ=UTC
# Optional: labels for Traefik reverse proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`portainer.yourdomain.com`)"
- "traefik.http.routers.portainer.tls=true"
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
volumes:
portainer_data:
EOF
Security note: We mount /var/run/docker.sock read-only. Portainer needs this to communicate with the Docker daemon. In production, consider using the Portainer Agent on remote hosts instead of direct socket mounts.
Step 2: Deploy the Stack
docker compose up -d
Pull and start the container:
[+] Running 2/2
✔ Network portainer_default Created
✔ Container portainer Started
Verify it's running:
docker ps --filter name=portainer
CONTAINER ID IMAGE STATUS PORTS
abc123def456 portainer/portainer-ce:latest Up 30 seconds 0.0.0.0:8000->8000/tcp, 0.0.0.0:9443->9443/tcp
Step 3: Initial Setup and Admin Account
Open your browser to https://YOUR_SERVER_IP:9443 (note: HTTPS, not HTTP).
You'll see the initial setup screen:
- Create admin user: Username
admin, strong password (12+ chars, mixed case, symbols) - Select environment: Choose "Get Started" → "Live connect to the local Docker environment"
- Done: You'll land on the dashboard showing running containers, images, volumes, and networks
Critical: If you skip creating the admin user within 5 minutes of first start, Portainer auto-creates one with a random password. If this happens, restart the container: docker restart portainer.
Step 4: Verify Core Functionality
Run through these checks to confirm everything works:
4.1 View Running Containers
Click Containers in the left sidebar. You should see Portainer itself listed with status "running", CPU/memory usage, and published ports.
4.2 Inspect Container Logs
Click any container → Logs tab. Logs stream in real-time with search and download options. No more docker logs -f.
4.3 Access Container Console
Click a container → Console button → Select /bin/sh or /bin/bash. You now have an interactive shell inside the container, directly from your browser.
4.4 Manage Networks
Go to Networks → Click any network → See all connected containers and their IP addresses. Visualize your stack's network topology instantly.
Step 5: Deploy Your First Stack (Git Integration)
Portainer's killer feature: deploy entire docker-compose stacks from Git repositories.
- Go to Stacks → Add stack
- Name:
uptime-kuma - Build method: Repository
- Repository URL:
https://github.com/louislam/uptime-kuma - Compose path:
docker/docker-compose.yml - Click Deploy the stack
Portainer clones the repo, reads the compose file, and deploys the entire stack. Update it later by clicking Pull and redeploy—zero manual file editing.
Step 6: Add a Remote Environment (Optional but Powerful)
Manage multiple servers from one Portainer instance:
On the remote server:
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
In Portainer UI:
- Environments → Add environment
- Select Docker environment → Agent
- Name:
prod-vps-01 - Environment address:
REMOTE_SERVER_IP:9001 - Click Connect
Your remote server now appears in the environment dropdown. Switch between local and remote with one click.
Production Hardening Checklist
- Reverse proxy: Put Portainer behind Traefik or NGINX with valid SSL certificates. Never expose port 9443 directly to the internet.
- Firewall: Restrict port 8000/9443 to your IP or internal network only.
- Backups: The
portainer_datavolume contains all configuration. Back it up:docker run --rm -v portainer_data:/data -v $(pwd):/backup alpine tar czf /backup/portainer-backup.tar.gz -C /data . - LDAP/OAuth: In Settings → Authentication, connect to your existing identity provider instead of local accounts.
- Edge Compute: For remote IoT/edge devices, use the Edge Agent (lighter, works over outbound HTTPS).
Common Issues and Fixes
| Issue | Fix |
|---|---|
| "Unable to connect to Docker" | Ensure /var/run/docker.sock exists and user has docker group permissions |
| HTTPS certificate warning | Portainer generates a self-signed cert by default. Use a reverse proxy with Let's Encrypt for valid certs. |
| Container logs empty | Some images log to files instead of stdout. Check the app's logging configuration. |
| High memory usage | Portainer itself uses ~50MB. High usage usually means too many containers/images. Prune unused resources. |
Next Steps
Now that Portainer is running, level up your homelab:
- Monitor everything: Deploy Uptime Kuma to track service health
- Secure access: Add Authelia for SSO and 2FA in front of Portainer
- Automate deployments: Set up n8n workflows triggered by Git webhooks to auto-redeploy stacks
- Centralize logs: Forward container logs to Grafana Loki for long-term storage and querying
Conclusion
Portainer transforms Docker from a command-line chore into a visual, manageable system. In 15 minutes, you've deployed a production-ready container management platform that scales from a single VPS to a multi-node infrastructure.
The real power isn't just the UI—it's the ability to define infrastructure as code (docker-compose files in Git) and deploy consistently across environments. Combine Portainer with a solid reverse proxy and monitoring stack, and you have a professional-grade platform running on your own hardware.
Questions or stuck on a step? Drop a comment below or reach out on Discord.
Last updated: September 6, 2026. Portainer CE version referenced: 2.21.x.