How to Install Portainer and Manage Docker on a Linux VPS

Portainer is a web dashboard for the Docker engine already running on your VPS. Its documentation describes the goal as hiding the complexity of managing containers behind an easy-to-use UI, so everyday container work doesn't depend on remembering CLI flags. That is a real convenience once a server runs more than a handful of containers. It is also a real responsibility, because anything that can drive Docker can drive the whole machine. This guide installs the free Community Edition with two commands and gets you through the first-login window. It then tours the screens you will use most and covers the hardening that belongs in the install rather than after it.

Quick Verdict
Portainer Community Edition runs as one container from the portainer/portainer-ce image. It uses a single Docker volume for its own data, and the host's Docker socket is mounted in so it can see and control everything else. The install is two commands, but the first admin login is time-limited: a Tencent Cloud walkthrough puts the window at five minutes after the container first starts. The trade-off is power. A Portainer login is effectively a root login on the server, so keep the interface off the open internet and use a strong password with two-factor authentication. The project's own README positions CE for homelabs and personal projects rather than supported production use.

What Portainer Is, and Which Edition You're Installing

Portainer comes in two editions. Business Edition is the commercial one. The docs list role-based access control, registry management and dedicated support among its features, and it needs a license key, although Portainer offers three nodes free or you can buy a license. Community Edition (CE) is the free, open-source option that the docs aim at home users and hobbyists, and it does not include everything Business Edition does. CE is what this guide installs.

Read the project README on GitHub before you decide CE is enough for a given server. It says CE is designed for homelabs, learning environments and personal projects. It is updated periodically with no guaranteed cadence, and it comes without support, warranty or an SLA. Help comes from other users through GitHub Issues and community chat, on a voluntary, best-effort basis. For production or business-critical use, the README points to Business Edition, which adds features such as RBAC, GitOps, edge management, audit and SSO.

At the time of writing, the documentation site is labelled 2.45 LTS, so that is the reference to check when a screen on your install differs from the ones described below. Portainer can also manage Kubernetes and Podman environments, among others. This guide covers the most common VPS case, one Docker host managed from its own dashboard.

Before You Start

You need a Linux VPS with Docker installed and running, an SSH login with sudo rights and, only if you want a permanent HTTPS address later, a domain name you can point at the server. How-To Geek's Portainer tutorial opens with the same prerequisite: Docker installed and running before anything else. Check that a working docker command exists:

1sudo docker --version
2sudo docker ps

If docker ps prints a table, even an empty one, the daemon is reachable. If it doesn't, set Docker up first, because everything below assumes it works.

The Nginx and Certbot commands later use apt, so they assume a distribution that uses it, such as Ubuntu. The Tencent Cloud walkthrough this guide draws on targets Ubuntu 22.04. On other distributions, substitute your package manager's equivalents.

Installing Portainer CE with Docker

Portainer stores all of its application data in one Docker volume, so create that first:

1sudo docker volume create portainer_data

Then start the container:

1sudo docker run -d \
2  --name portainer \
3  --restart unless-stopped \
4  -p 127.0.0.1:9000:9000 \
5  -v /var/run/docker.sock:/var/run/docker.sock \
6  -v portainer_data:/data \
7  portainer/portainer-ce:latest

Each part does one job:

  • -d runs the container in the background.
  • --name portainer gives it a predictable name for later docker restart and docker logs commands.
  • --restart unless-stopped brings it back after a reboot or a Docker restart, unless you stopped it yourself.
  • -v /var/run/docker.sock:/var/run/docker.sock mounts the host's Docker socket into the container. That is how Portainer reaches your machine's Docker instance and everything running on it.
  • -v portainer_data:/data mounts the volume you just created at /data, where Portainer keeps its data.
  • portainer/portainer-ce:latest is the Community Edition image. latest resolves to the newest image at the moment it is first pulled. If you want upgrades to stay predictable, pin a specific version tag from Portainer's release notes instead.

The -p line needs its own explanation. Per the Tencent Cloud walkthrough, port 9000 is Portainer's plain-HTTP interface. Putting 127.0.0.1: in front publishes it on the server's loopback address only, so nothing outside the VPS can reach it directly. That walkthrough's own command also publishes 9443 (Portainer's built-in HTTPS interface) and 8000 (which it describes as used for remote management) on every network interface. Its security section then advises against exposing 9000 or 9443 directly. Binding to loopback from the start is the consistent version of that advice, and neither extra port is needed for one server reached through a tunnel or a reverse proxy. If you would rather use the built-in HTTPS listener with no proxy, add -p 9443:9443. In that case, load the setup page immediately, and don't assume a host firewall rule covers a published container port. Test from another machine.

Confirm the container is running:

1sudo docker ps | grep portainer

The status column should read Up.

Reaching the Interface and Setting the Admin Password

With the port bound to loopback, an SSH tunnel is the simplest way in. It needs no domain or certificate, and nothing is exposed. On your own computer, not the VPS, run:

1ssh -L 9000:127.0.0.1:9000 youruser@YOUR_SERVER_IP

Leave that session open and browse to http://localhost:9000. The traffic between your browser and the server rides inside the encrypted SSH connection, so the plain-HTTP page never crosses the open internet.

Do this promptly. Portainer deliberately locks an uninitialised instance after a short window. According to the Tencent Cloud walkthrough, that is five minutes after the container first starts. After that the page reports that the instance "timed out for security purposes." If you miss the window, restart the container and go straight back to the page:

1sudo docker restart portainer

On the setup screen, choose an admin username and a long, unique password. The same walkthrough notes that Portainer has no account lockout by default, so the password is doing all the work. Portainer then asks which environment to manage. Pick the local Docker instance and connect. Depending on your version, you may instead land on a Home screen with a local environment tile, as the How-To Geek tutorial describes. Click it to open the dashboard, which summarises your containers, images and volumes. If you forget the admin password later, the docs' Advanced Topics section includes a page on resetting it.

Adding a Permanent HTTPS Address with Nginx

A tunnel is fine for occasional use, but if you are in Portainer daily you will want an address. The Tencent Cloud walkthrough uses a reverse proxy, and it is the pattern worth copying. Nginx handles ports 80 and 443 and forwards to Portainer on the loopback port you already bound. Portainer never listens on a public interface, so the proxy is the only way in.

Point a DNS record for a name such as portainer.yourdomain.com at the VPS's IP address. Then install Nginx and create the site file:

1sudo apt install -y nginx
2sudo nano /etc/nginx/sites-available/portainer

Contents of the file:

 1server {
 2    listen 80;
 3    server_name portainer.yourdomain.com;
 4
 5    location / {
 6        proxy_pass http://127.0.0.1:9000;
 7        proxy_http_version 1.1;
 8        proxy_set_header Upgrade $http_upgrade;
 9        proxy_set_header Connection 'upgrade';
10        proxy_set_header Host $host;
11        proxy_set_header X-Real-IP $remote_addr;
12        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
13        proxy_set_header X-Forwarded-Proto $scheme;
14        proxy_cache_bypass $http_upgrade;
15    }
16}

Enable the site, test the syntax and reload:

1sudo ln -s /etc/nginx/sites-available/portainer /etc/nginx/sites-enabled/
2sudo nginx -t && sudo systemctl reload nginx

Next, get a certificate from Let's Encrypt through Certbot. Its Nginx plugin can adjust the site file to serve HTTPS:

1sudo apt install -y certbot python3-certbot-nginx
2sudo certbot --nginx -d portainer.yourdomain.com

Then open only what the proxy needs. Allow SSH first so you can't lock yourself out:

1sudo ufw allow ssh
2sudo ufw allow 'Nginx Full'
3sudo ufw enable

Visit https://portainer.yourdomain.com and log in with the account you created. To tighten things further, add an allow and deny pair at the top of the location / block so only your own address can reach the login page:

1        allow YOUR_IP;
2        deny all;

Portainer's docs also have a page on running it behind reverse proxies, under Advanced Topics, if you would rather use a different proxy.

A Tour of the Container Screens

Open the local environment from Home and click Containers in the sidebar. You get a table of every container on the host, with running ones marked green and stopped ones red. Tick the checkbox beside a container and the buttons above the table start, stop, restart or remove it. One caution from the How-To Geek tutorial applies here: Portainer's own container appears in that list, and stopping it takes down the very interface you are using.

Click a container's name for its detail page. The Portainer docs list what you can do from there: inspect it, edit or duplicate it, toggle a webhook, attach volumes, view logs and statistics, change ownership and open a console. Three of those earn their keep daily:

  • Logs. The Tencent Cloud walkthrough notes you can filter by keyword, toggle timestamps, download the log file and auto-scroll to the latest lines.
  • Stats. Live resource usage per container makes it easy to spot the one eating your VPS's memory before the kernel starts choosing victims for you.
  • Console. Connect opens a web-based terminal inside the running container, so you can debug without a separate SSH session. It is a shell in the container, not on the host.

If the new image indicator feature is enabled on your install, the "Images up to date" column marks each container. A green tick means the local image is current, an orange cross means the remote registry has a newer version, and a grey hyphen means Portainer could not tell. The reload button next to the search box rechecks them all.

The docs also make a point that shapes how you should use all of this. Containers hold no persistent data, so they can be destroyed and recreated as needed. Anything that has to survive, such as a database, uploads or configuration, belongs in a volume. That is why the docs give attaching a volume its own page.

Creating a Container from the Form

For a one-off container, click Add container on the Containers screen. Name it, then type the image. Public images from Docker Hub, such as nginx:latest, need nothing else. For a private registry, add its URL, username and password first under Registries in the sidebar, then choose it from the Registry dropdown on the creation form. According to How-To Geek, the same screen can hold your Docker Hub credentials. That lets you pull private images and avoids the rate limits applied to unauthenticated users.

Above the deploy button you will find port binding, an option to force an image pull before deploying, and an option to remove the container automatically when it exits. The advanced settings below replicate the whole docker run command line: command, entrypoint, volumes, networks and environment variables. Editing an existing container uses the same form through Duplicate/Edit, but know what it does. It destroys the container and replaces it with a new one carrying the changed properties, which is safe precisely because the data lives in volumes. Menu names shift between releases, and that tutorial dates from 2021. Treat its layout as a guide and the current docs as the authority.

Deploying a Stack from a Compose File

Most self-hosted apps arrive as several linked containers, such as an app plus a database, and Portainer's Stacks feature deploys them together from a docker-compose.yml file. As the How-To Geek tutorial explains, there is no graphical stack builder. You paste a Compose file, upload one, or point Portainer at a Git repository and use the file in it. You can set environment variables before deploying, and afterwards you can stop or delete all of a stack's containers together from the Stacks screen.

Try it with a deliberately boring example. In Stacks, choose Add stack, name it demo-web, pick the web editor and paste:

1services:
2  web:
3    image: nginx:latest
4    restart: unless-stopped
5    ports:
6      - "127.0.0.1:8080:80"

Click Deploy the stack. Per the Tencent Cloud walkthrough, Portainer runs docker compose up -d with your configuration, and you can change a running stack by editing the file in Portainer and choosing Update the stack. Confirm the container answers on the VPS:

1curl -I http://127.0.0.1:8080

An HTTP 200 response means it is serving. Then delete the stack from the Stacks screen, since the point was the workflow and not a permanent web server. Portainer also ships built-in app templates, reachable from the App Templates entry in the sidebar, for spinning up common services faster. You can turn a stack into a reusable template of your own too.

Managing More Than One Server

Portainer is not limited to the host it runs on. Per the Tencent Cloud walkthrough, you run the Portainer Agent as a container on each additional server. You then add it under Environments, Add environment, Agent, using that server's IP address and port 9001. The docs' "Add an environment to an existing installation" page is the current reference for the exact steps. Because an agent fronts that host's Docker socket, restrict which addresses can reach port 9001 rather than leaving it open to everyone.

Locking It Down

Everything so far shares one theme, and the Tencent Cloud walkthrough states it bluntly. Portainer has full access to your Docker daemon, so anyone who can log in can run arbitrary containers, mount host filesystems and effectively get root on the server. Treat the login accordingly:

  • Keep it off the open internet. Loopback binding plus a tunnel or proxy means there is no Portainer port for a scanner to find.
  • Use a long, unique admin password and turn on two-factor authentication. The walkthrough points to Admin, My Account, Two-factor authentication. The docs have an Account settings section if your version keeps it elsewhere.
  • Limit who gets an administrator account. The docs list role-based access control among Business Edition features, so on CE the practical control is to hand out as few admin logins as you can.
  • Review Settings. The How-To Geek tutorial points there for security options, a custom logo and opting out of anonymous usage statistics. Its Authentication area lets you replace the built-in user database with an existing LDAP server or OAuth provider.
  • Back up the volume, not the container. The Tencent Cloud FAQ's advice is that persistent data lives in named volumes, so portainer_data is the thing worth backing up. Follow the docs' Updating Portainer page for upgrades. Your settings carry across because they live in the volume, not in the container you replace.

Who This Is Worth Setting Up For

Good fit:

  • Solo admins and hobbyists running a handful or more of containers who want status, logs and a shell in one browser tab
  • Anyone deploying from Compose files who wants a place to edit and update stacks, including from a Git repository
  • Homelab, learning and personal-project servers, which is the use the README describes for CE

Not the best fit:

  • Production or business-critical workloads that need guaranteed support, which the README directs to Business Edition
  • Servers where you cannot keep the interface behind a tunnel, a proxy or an IP restriction, given that the login is effectively root
  • Setups managed entirely through the CLI or automation, where a second control surface on the box adds more risk than convenience

Two Commands, Then the Careful Part

The install itself is a volume and a container. The careful part is everything after it: reaching the interface without exposing it, finishing the first login before the window closes, and treating the account like the root login it effectively is. Do those three things and Portainer delivers what its docs promise. Containers, logs, consoles and Compose stacks sit in a browser tab, and you can stay on the command line for everything else.

References

Posts in this series