Docker transformed how we deploy applications, but managing containers entirely from the command line can feel like navigating a cockpit with no instruments. You know the containers are running — somewhere — but checking logs means remembering syntax, restarting a service means finding the right container ID, and understanding resource usage means juggling multiple terminal windows. Portainer solves this by giving you a full-featured web dashboard for managing Docker environments — containers, images, volumes, networks, and even multi-host deployments — all from your browser.
Whether you're a developer who prefers visual interfaces, a team lead who needs to give colleagues container access without teaching them CLI, or a solo operator managing several services on a VPS, Portainer is one of the most useful tools you can install. It runs as a single lightweight container, adds virtually no overhead, and can be deployed in under two minutes.
MassiveGRID Ubuntu VPS includes: Ubuntu 24.04 LTS pre-installed · Proxmox HA cluster with automatic failover · Ceph 3x replicated NVMe storage · Independent CPU/RAM/storage scaling · 12 Tbps DDoS protection · 4 global datacenter locations · 100% uptime SLA · 24/7 human support rated 9.5/10
Deploy a self-managed VPS — from $1.99/mo
Need dedicated resources? — from $19.80/mo
Want fully managed hosting? — we handle everything
What Portainer Does (And Who It's For)
Portainer is an open-source container management platform that provides a web-based GUI for Docker, Docker Swarm, and Kubernetes environments. The Community Edition (CE) is free and covers everything most self-hosters and small teams need.
With Portainer, you can:
- View all running containers, their status, resource usage, and logs in one dashboard
- Start, stop, restart, and remove containers with a single click
- Deploy new containers from Docker Hub images using a form-based interface
- Create and manage Docker Compose stacks directly in the browser
- Access a web-based terminal console inside any running container
- Manage Docker volumes, networks, and images
- Set up user accounts with role-based access control
- Connect to multiple Docker hosts from a single Portainer instance
Portainer is particularly valuable for these use cases:
- Developers who prefer GUIs — not everyone wants to memorize
docker logs --tail 100 -f container_name - Teams with mixed skill levels — give a colleague access to restart their app without SSH credentials
- Multi-service VPS management — when you're running 5-10 services and need a quick overview
- Learning Docker — the visual interface helps you understand Docker concepts faster
Prerequisites
Before installing Portainer, you need:
- An Ubuntu 24.04 VPS with root or sudo access — see our Ubuntu VPS setup guide if you're starting fresh
- Docker and Docker Compose installed — follow our Docker installation guide to get both set up
- A domain name pointed to your VPS (optional, but recommended for SSL access)
Verify Docker is running:
docker --version
docker compose version
sudo systemctl status docker
You should see Docker Engine 24.x+ and Docker Compose v2.x+ with the Docker service active and running.
Method 1: Deploy Portainer with Docker CLI (Quick Install)
The fastest way to get Portainer running is a single Docker command. First, create a volume to persist Portainer's data (user accounts, settings, stack definitions):
sudo docker volume create portainer_data
Now deploy the Portainer CE container:
sudo docker run -d \
--name portainer \
--restart=always \
-p 8000:8000 \
-p 9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:2.21-latest
Let's break down each flag:
-d— runs the container in detached mode (background)--restart=always— automatically restarts the container after reboots or crashes-p 8000:8000— exposes the Edge Agent communication port (needed for multi-host setups)-p 9443:9443— exposes the Portainer web UI on HTTPS-v /var/run/docker.sock:/var/run/docker.sock— gives Portainer access to the Docker daemon-v portainer_data:/data— persists Portainer configuration data
Verify the container is running:
sudo docker ps --filter name=portainer
You should see the container listed with status "Up." Portainer is now accessible at https://your-server-ip:9443.
Note: Portainer uses a self-signed SSL certificate by default on port 9443. Your browser will show a security warning — this is expected. We'll fix this by adding a proper SSL certificate via Nginx reverse proxy later in this guide.
Method 2: Deploy Portainer with Docker Compose (Recommended)
For production use, Docker Compose gives you a version-controlled, reproducible deployment. Create a directory for the Portainer stack:
sudo mkdir -p /opt/portainer
sudo nano /opt/portainer/docker-compose.yml
Add the following configuration:
services:
portainer:
image: portainer/portainer-ce:2.21-latest
container_name: portainer
restart: always
security_opt:
- no-new-privileges:true
ports:
- "8000:8000"
- "9443:9443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
environment:
- TZ=UTC
volumes:
portainer_data:
driver: local
Deploy the stack:
cd /opt/portainer
sudo docker compose up -d
Check the logs to confirm successful startup:
sudo docker compose logs -f portainer
You should see output indicating the server has started successfully on port 9443. Press Ctrl+C to exit the log stream.
Securing Portainer Behind Nginx Reverse Proxy with SSL
Accessing Portainer directly on port 9443 with a self-signed certificate isn't ideal for production. A much better approach is to put Portainer behind Nginx with a Let's Encrypt SSL certificate. If you don't have Nginx installed yet, see our Nginx reverse proxy guide.
First, modify the Docker Compose file to only expose Portainer on localhost. Update /opt/portainer/docker-compose.yml:
services:
portainer:
image: portainer/portainer-ce:2.21-latest
container_name: portainer
restart: always
security_opt:
- no-new-privileges:true
ports:
- "127.0.0.1:9443:9443"
- "8000:8000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
environment:
- TZ=UTC
volumes:
portainer_data:
driver: local
Recreate the container with the updated port binding:
cd /opt/portainer
sudo docker compose up -d
Now create the Nginx virtual host. Create a new configuration file:
sudo nano /etc/nginx/sites-available/portainer.example.com
Add the following configuration (replace portainer.example.com with your actual domain):
server {
listen 80;
server_name portainer.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name portainer.example.com;
ssl_certificate /etc/letsencrypt/live/portainer.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/portainer.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
client_max_body_size 100M;
location / {
proxy_pass https://127.0.0.1:9443;
proxy_ssl_verify off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
}
}
Note the proxy_pass https:// — Portainer's internal server uses HTTPS even locally, and proxy_ssl_verify off tells Nginx to accept the self-signed certificate for the backend connection. The WebSocket headers (Upgrade and Connection) are important because Portainer uses WebSockets for the container console feature.
Enable the site and obtain an SSL certificate using Let's Encrypt (see our Let's Encrypt guide for detailed instructions):
sudo ln -s /etc/nginx/sites-available/portainer.example.com /etc/nginx/sites-enabled/
sudo certbot --nginx -d portainer.example.com
sudo nginx -t
sudo systemctl reload nginx
Now access Portainer at https://portainer.example.com with a valid SSL certificate.
Creating Your Admin Account and Securing Access
When you first access Portainer, you'll see a setup wizard. This is time-sensitive — Portainer requires you to create an admin account within 5 minutes of the first startup, or it will lock itself for security.
If you missed the window:
cd /opt/portainer
sudo docker compose restart portainer
On the setup screen:
- Enter a username (default is
admin, but consider something less predictable) - Set a strong password (minimum 12 characters required)
- Click "Create user"
After creating the admin account, you'll be asked to connect Portainer to your Docker environment. Select "Get Started" to automatically connect to the local Docker instance (the one whose socket you mounted). You should immediately see your local environment listed with the Portainer container running.
Disabling New User Registration
By default, Portainer does not allow open registration after the initial admin is created. But verify this explicitly: navigate to Settings → Authentication and confirm that the authentication method is set to "Internal" and that no public sign-up option is enabled.
Adding Team Members
If colleagues need access, create accounts manually: go to Users → Add user. Portainer CE supports two roles:
- Administrator — full control over all environments and settings
- User — access only to environments and resources you explicitly grant
For team environments, create individual user accounts and assign them to specific environments rather than sharing the admin credentials.
Deploying Your First Stack from a Template
Portainer's "App Templates" feature lets you deploy popular applications with a few clicks. Navigate to App Templates in the sidebar. You'll find templates for common services like Nginx, MySQL, PostgreSQL, Redis, WordPress, and more.
To deploy a template:
- Click on the application (e.g., "Nginx")
- Give the container a name
- Optionally configure network and volume settings
- Click "Deploy the container"
While templates are convenient for quick experiments, for production workloads, you'll want to use custom stacks with Docker Compose files — which brings us to the most powerful feature of Portainer.
Managing Docker Compose Stacks Through the GUI
Stacks are Portainer's term for Docker Compose deployments, and this is where Portainer truly shines. Instead of SSH-ing into your server and editing YAML files, you can create, edit, and manage entire multi-container applications from the web interface.
Creating a New Stack
Navigate to Stacks → Add stack. You have several input methods:
- Web editor — paste or type your Docker Compose YAML directly in the browser
- Upload — upload a docker-compose.yml file from your computer
- Git repository — pull the Compose file from a Git repo (supports auto-updates)
- Custom template — use a previously saved template
Let's create a practical example — a full WordPress stack with MySQL. In the web editor, enter:
services:
wordpress:
image: wordpress:6.7-php8.3-apache
container_name: wordpress_app
restart: always
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: wordpress_db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress_data:/var/www/html
depends_on:
- wordpress_db
wordpress_db:
image: mysql:8.0
container_name: wordpress_db
restart: always
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
volumes:
- db_data:/var/lib/mysql
volumes:
wordpress_data:
db_data:
Below the editor, you'll see an "Environment variables" section. Add:
DB_PASSWORD— your WordPress database passwordDB_ROOT_PASSWORD— your MySQL root password
This is one of Portainer's best features — environment variables are stored securely and separately from the Compose file, so you can share stack definitions without exposing secrets.
Click "Deploy the stack" and Portainer will pull the images and start the containers. You can watch the deployment progress in real time.
Editing an Existing Stack
To modify a running stack, go to Stacks, click on the stack name, and select the "Editor" tab. Make your changes to the YAML, then click "Update the stack." Portainer will intelligently recreate only the containers that changed — the same behavior as running docker compose up -d after editing a file.
Git-Based Stacks (GitOps Workflow)
For production environments, the Git repository option is powerful. Point Portainer to a Git repo containing your docker-compose.yml, and it can:
- Pull the initial configuration on stack creation
- Automatically poll for changes and redeploy (configurable interval)
- Use a specific branch or tag
- Authenticate with private repositories
This creates a simple GitOps workflow: push a change to your repo, and Portainer detects and deploys it automatically.
Container Logs, Stats, and Console Access
These three features alone justify installing Portainer for many users.
Container Logs
Click on any container, then click "Logs." You get a real-time log viewer with:
- Auto-refresh toggle
- Line wrapping
- Timestamp display
- Search/filter within logs
- Download logs as a file
- Configurable number of lines to display
This replaces docker logs -f --tail 500 container_name with something far more usable — especially when you need to search through output or share log excerpts with a team member.
Container Stats
The "Stats" view shows real-time graphs for:
- CPU usage (percentage)
- Memory usage (actual vs. limit)
- Network I/O (bytes in/out)
- Block I/O (disk reads/writes)
This is the equivalent of running docker stats in a terminal, but presented as visual graphs that update every few seconds. It's invaluable for spotting memory leaks, CPU spikes, or unexpected network activity.
Console Access
Click "Console" on any container to open a web-based terminal session inside the container. You can choose between /bin/bash, /bin/sh, or a custom command. This replaces docker exec -it container_name /bin/bash and works entirely in the browser — no SSH needed.
This is particularly useful for debugging: you can quickly check file contents, test network connectivity from inside a container, or inspect the running process.
Portainer Edge Agents for Multi-Server Management
If you manage Docker environments across multiple servers — for example, a production server, a staging server, and a development server — Portainer's Edge Agent feature lets you manage all of them from a single Portainer instance.
The architecture works like this:
- Portainer Server — your main Portainer instance (runs on one VPS)
- Edge Agent — a lightweight agent container deployed on each remote server
Edge Agents initiate an outbound connection to the Portainer Server, meaning remote servers don't need any inbound ports opened for management — the agent connects out through port 8000.
Setting Up an Edge Agent
In your Portainer dashboard, go to Environments → Add environment → Edge Agent. Portainer will generate a deployment command specific to your instance. It looks like this:
docker run -d \
--name portainer_edge_agent \
--restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /var/lib/docker/volumes:/var/lib/docker/volumes \
-v /:/host \
-v portainer_agent_data:/data \
-e EDGE=1 \
-e EDGE_ID=your-unique-edge-id \
-e EDGE_KEY=your-edge-key \
-e EDGE_INSECURE_POLL=1 \
-e PORTAINER_HOST=https://portainer.example.com \
-e PORTAINER_PORT=443 \
portainer/agent:2.21-latest
Run this command on each remote server you want to manage. Within a minute, the environment will appear in your Portainer dashboard, and you can manage it exactly like your local Docker environment — stacks, containers, logs, console, everything.
Performance note: Portainer adds virtually no overhead — the server runs in under 100MB RAM alongside your application containers. Edge agents use even less. Deploy a Cloud VPS to run Portainer as your central management hub.
Managing Images and Cleaning Up
Over time, Docker accumulates unused images, stopped containers, and orphaned volumes. Portainer makes cleanup visible and manageable.
Navigate to Images to see every Docker image on your server, including:
- Image name and tag
- Size on disk
- Creation date
- Whether it's currently in use by a container
You can select unused images and remove them in bulk. For a more aggressive cleanup, the "Remove all unused images" button is equivalent to running:
docker image prune -a
Similarly, the Volumes page shows all Docker volumes with their sizes and which containers reference them. Orphaned volumes (not attached to any container) can be identified and removed. For managing overall disk space on your VPS, see our disk space management guide.
Portainer vs Coolify vs Dokploy — When to Use What
Portainer is not the only web-based Docker management tool. Two popular alternatives are Coolify and Dokploy, both of which overlap with Portainer but serve different primary use cases.
| Feature | Portainer CE | Coolify | Dokploy |
|---|---|---|---|
| Primary purpose | Docker environment management | Self-hosted PaaS (Heroku alternative) | Self-hosted PaaS (deployment platform) |
| Git push-to-deploy | Limited (Git-based stacks) | Yes (core feature) | Yes (core feature) |
| Automatic SSL | No (manual/external) | Yes (built-in via Traefik) | Yes (built-in via Traefik) |
| Database management | Deploy via containers | One-click databases | One-click databases |
| Docker Compose support | Full (native) | Yes | Yes |
| Container-level control | Full (logs, stats, console, exec) | Basic | Basic |
| Multi-host management | Yes (Edge Agents) | Yes (remote servers) | Yes (multi-server) |
| Kubernetes support | Yes | No | No |
| Resource overhead | ~50-100MB RAM | ~300-500MB RAM | ~200-400MB RAM |
| Best for | Managing Docker infrastructure | Deploying web applications | Deploying web applications |
Choose Portainer when:
- You want to manage existing Docker infrastructure visually
- You need deep container-level control (exec, logs, stats, networking)
- You're managing multiple Docker hosts from one place
- You want a lightweight management layer, not a deployment platform
- You're already comfortable with Docker Compose and just want a better UI
Choose Coolify or Dokploy when:
- You want a Heroku/Vercel-like experience — push code, get a running app
- You need automatic SSL certificate management
- You want one-click database deployments with managed backups
- Your workflow is "deploy applications" rather than "manage containers"
Portainer and Coolify/Dokploy are not mutually exclusive. Some operators run Portainer alongside Coolify to get the best of both worlds — Coolify for application deployment, Portainer for infrastructure visibility.
Updating Portainer
Portainer is updated by pulling the latest image and recreating the container. Your data persists in the Docker volume.
If you used Docker Compose:
cd /opt/portainer
sudo docker compose pull
sudo docker compose up -d
If you used the single Docker run command:
sudo docker stop portainer
sudo docker rm portainer
sudo docker pull portainer/portainer-ce:2.21-latest
sudo docker run -d \
--name portainer \
--restart=always \
-p 8000:8000 \
-p 127.0.0.1:9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:2.21-latest
Your admin account, stack definitions, environment configurations, and all settings are preserved in the portainer_data volume.
Backing Up Portainer
Portainer stores its entire configuration in the Docker volume. To back it up:
# Stop Portainer temporarily
cd /opt/portainer
sudo docker compose stop portainer
# Back up the volume
sudo docker run --rm \
-v portainer_data:/data \
-v /opt/backups:/backup \
ubuntu:24.04 \
tar czf /backup/portainer-backup-$(date +%Y%m%d).tar.gz -C /data .
# Restart Portainer
sudo docker compose start portainer
To restore from a backup:
# Stop Portainer
sudo docker compose stop portainer
# Clear existing data and restore
sudo docker run --rm \
-v portainer_data:/data \
-v /opt/backups:/backup \
ubuntu:24.04 \
bash -c "rm -rf /data/* && tar xzf /backup/portainer-backup-20260228.tar.gz -C /data"
# Start Portainer
sudo docker compose start portainer
For automated backup strategies, see our automatic backups guide.
Security Best Practices
Portainer has access to the Docker socket, which effectively means root-level access to your server. Take these precautions:
1. Always Use HTTPS
Never expose Portainer over plain HTTP. The Nginx reverse proxy with SSL approach described above is the minimum. If you access Portainer over HTTP, your admin credentials travel in plain text.
2. Restrict Access by IP (Optional)
If only specific IP addresses need Portainer access, add IP restriction to the Nginx configuration:
location / {
allow 203.0.113.10; # Your office IP
allow 198.51.100.20; # Your home IP
deny all;
proxy_pass https://127.0.0.1:9443;
# ... rest of proxy config
}
3. Use Strong Passwords and Consider 2FA
Portainer Business Edition supports OAuth and LDAP. For CE, use a strong, unique password for the admin account. You can add an extra authentication layer at the Nginx level with HTTP basic auth as a second factor.
4. Keep Portainer Updated
Portainer handles Docker socket communication — security patches matter. Check for updates monthly and apply them promptly.
For comprehensive VPS security practices, review our security hardening guide.
Troubleshooting Common Issues
Portainer Shows "Waiting for Edge Agent" for Local Environment
This usually means the Docker socket mount is incorrect. Verify:
sudo ls -la /var/run/docker.sock
The socket should exist and be owned by the docker group. If using rootless Docker, the socket path will be different.
Cannot Connect to Portainer After Reboot
Check that both Docker and the Portainer container started:
sudo systemctl status docker
sudo docker ps -a --filter name=portainer
If the container is in "Exited" state, start it manually and check logs:
sudo docker start portainer
sudo docker logs portainer
WebSocket Errors in Container Console
If the in-browser console doesn't connect, the Nginx configuration is likely missing the WebSocket upgrade headers. Ensure these lines are in your Nginx location block:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Stacks Show as "Limited" After Portainer Restart
This happens when stacks were originally deployed outside of Portainer (via CLI docker compose up). Portainer can detect these running services but cannot fully manage them. The solution is to redeploy through Portainer's stack interface — copy the Compose file content, create a new stack, and deploy.
Prefer Managed Container Hosting?
Portainer gives you powerful visual management for self-hosted Docker environments. But if you'd rather not manage the underlying infrastructure at all — operating system updates, security patches, Docker daemon maintenance, backup automation — MassiveGRID's fully managed dedicated cloud servers handle the entire stack for you. Your containers run on dedicated resources with high-availability failover, and the MassiveGRID team manages everything underneath.
Scaling up? Managing 10+ containers across multiple stacks benefits from dedicated resources that ensure predictable performance. MassiveGRID Dedicated VPS plans start at $19.80/mo with guaranteed CPU, RAM, and NVMe storage that never competes with other tenants.
Summary
Portainer transforms Docker management from a purely command-line experience into something accessible, visual, and team-friendly. To recap the key steps:
- Deploy Portainer CE as a single Docker container — it takes under 2 minutes
- Secure it behind Nginx with a proper SSL certificate
- Create your admin account immediately (5-minute window)
- Use Stacks to manage Docker Compose deployments through the browser
- Leverage container logs, stats, and console for day-to-day operations
- Add Edge Agents for multi-server management from a single dashboard
For monitoring your Portainer-managed infrastructure, consider setting up server monitoring alongside the container-level visibility Portainer provides. And if you're using Traefik instead of Nginx as your reverse proxy, our Traefik guide covers integrating it with Docker containers like Portainer.