{"id":82006,"date":"2026-06-19T07:30:00","date_gmt":"2026-06-19T14:30:00","guid":{"rendered":"https:\/\/dhblog.dream.press\/blog\/?p=82006"},"modified":"2026-08-25T20:55:49","modified_gmt":"2026-08-26T03:55:49","slug":"docker-and-portainer-vps","status":"publish","type":"post","link":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/","title":{"rendered":"How To Run Docker on a VPS With Portainer (2026 Guide)"},"content":{"rendered":"<div class=\"tldr-block\" style=\"display: none;\">\n\t<div class=\"svg\">\n\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 119.25 37.8\">\n\t<g>\n\t\t<g>\n\t\t\t<path fill=\"#ffffff\" d=\"M23.4,6.93h-8.1v24.57h-7.2V6.93H0V0h23.4v6.93Z\" \/>\n\t\t\t<path fill=\"#ffffff\" d=\"M45,24.57v6.93h-18.45V0h7.2v24.57h11.25Z\" \/>\n\t\t\t<path fill=\"#ffffff\"\n\t\t\t\td=\"M90.9,15.75c0,8.91-6.61,15.75-15.3,15.75h-12.6V0h12.6c8.68,0,15.3,6.84,15.3,15.75ZM83.97,15.75c0-5.4-3.42-8.82-8.37-8.82h-5.4v17.64h5.4c4.95,0,8.37-3.42,8.37-8.82Z\" \/>\n\t\t\t<path fill=\"#ffffff\"\n\t\t\t\td=\"M105.57,21.15h-3.42v10.35h-7.2V0h12.6c5.98,0,10.8,4.81,10.8,10.8,0,3.87-2.34,7.38-5.81,9.13l6.71,11.56h-7.74l-5.94-10.35ZM102.15,14.85h5.4c1.98,0,3.6-1.75,3.6-4.05s-1.62-4.05-3.6-4.05h-5.4v8.1Z\" \/>\n\t\t<\/g>\n\t\t<path\n\t\t\tfill=\"#0173ec\"\n\t\t\td=\"M53.97,37.8h-5.4l1.8-13.27h7.2l-3.6,13.27ZM49.02,12.55c0-2.34,1.93-4.27,4.27-4.27s4.27,1.94,4.27,4.27-1.93,4.27-4.27,4.27-4.27-1.94-4.27-4.27Z\"\n\t\t \/>\n\t<\/g>\n<\/svg>\n\t<\/div>\n\t<div class=\"tldr-wrap\">\n\t\t\n\n<p class=\"wp-block-paragraph\">Running <a href=\"https:\/\/www.dreamhost.com\/hosting\/vps\/docker\/\">Docker on a VPS<\/a> gives you portable, self-hosted apps while giving you direct control over the server and deployment setup. Adding Portainer puts a web UI in front of Docker so you don&#8217;t have to live in the terminal.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Portainer publishes no RAM minimum. A 4 GB VPS may suit a small stack, but size it from the combined requirements of the operating system, Docker daemon, and every container you plan to run. Portainer itself runs as a lightweight container \u2014 your apps, not the UI, decide how much RAM you need. Portainer Community Edition is free and open source, and once Docker and the firewall prep are done, the install itself is one volume create and one docker run command.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two gotchas to know before you start: Portainer waits only five minutes for the first admin account, and Docker publishes ports straight past UFW by default. This guide handles both in order \u2014 the install, the firewall, the sizing, the backups, and where Portainer earns its keep on day two.<\/p>\n\n\n\t<\/div>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Have you ever heard of <em>Dockerception<\/em>?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Well, hold onto your hat. We\u2019re going down the rabbit hole.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Portainer, the web UI you&#8217;re about to install on top of Docker, <em>is itself a Docker container<\/em>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It&#8217;s a tool that runs on the very thing it manages. You spin it up with one docker run line, and the first thing it does is reach back through a small hole in the host (a socket file) and ask the Docker daemon to show it every container on the box \u2014 including itself. From inside that browser tab, you manage all of your other Docker containers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>The UI you&#8217;re staring at shows up in its own container list.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On a VPS where you&#8217;re already paying for the box and already comfortable with SSH, Portainer slots in cleanly on top of Docker without changing how anything underneath works.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>That&#8217;s<\/em> the value.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A 4 GB box can be a starting point for Docker and Portainer, but the applications you add determine whether that capacity is enough. Think home automation, the family photo store, the AI assistant, and the newsletter you&#8217;ve been threatening to launch \u2014 on one server you actually own.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s how to get it running, lock it down, and start seeing value.<\/p>\n\n\n\n<h2 id=\"h-what-is-portainer-exactly\" class=\"wp-block-heading\">What Is Portainer, Exactly?<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1600\" height=\"829\" data-src=\"https:\/\/www.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x.webp\" alt=\"Portainer Architecture 1x\" class=\"wp-image-82009 lazyload\" data-srcset=\"https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x.webp 1600w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-300x155.webp 300w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-1024x531.webp 1024w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-768x398.webp 768w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-1536x796.webp 1536w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-600x311.webp 600w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-1200x622.webp 1200w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-730x378.webp 730w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-1460x756.webp 1460w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-784x406.webp 784w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-1568x812.webp 1568w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/01-Portainer-Architecture_1x-877x454.webp 877w\" data-sizes=\"(max-width: 1600px) 100vw, 1600px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1600px; --smush-placeholder-aspect-ratio: 1600\/829;\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Portainer is a web UI that runs as a Docker container and manages your other containers through the Docker socket.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It\u2019s essentially a browser-based front end (a Docker GUI) for the same Docker commands you&#8217;d otherwise type by hand, plus stack management, role-based access (if you upgrade to Business Edition), and a dashboard view of CPU and memory.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The socket connection is what makes it work. The bind looks like <code><strong>-v \/var\/run\/docker.sock:\/var\/run\/docker.sock<\/strong><\/code> in the install command, and that one line is doing all the heavy lifting. Without it, Portainer is a UI for an empty Docker host. With it, Portainer is the Docker host&#8217;s remote control.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One thing to keep in mind: any process that can reach <strong><code>\/var\/run\/docker.sock<\/code><\/strong> can control the Docker daemon, and <a target=\"_blank\" href=\"https:\/\/docs.docker.com\/engine\/security\/\">Docker&#8217;s own security docs<\/a> are blunt that only trusted users should be allowed to do that: a container started through the daemon can mount the host&#8217;s entire filesystem and alter it without restriction. That&#8217;s a <em>feature<\/em> when it&#8217;s Portainer doing the asking and a <em>problem<\/em> when it&#8217;s some container you spun up last week and forgot you&#8217;d given socket access to.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Treat the socket bind as a privileged capability, and audit every other container that wants one.<\/p>\n\n\n\n<div class=\"article-newsletter article-newsletter--gradient\">\n\n\n<h2>Get Content Delivered Straight to Your Inbox<\/h2><p>Subscribe now to receive all the latest updates, delivered directly to your inbox.<\/p><form class=\"nwsl-form\" id=\"newsletter_block_\" novalidate><div class=\"messages\"><\/div><div class=\"form-group\"><label for=\"input_newsletter_block_\"><input type=\"email\"name=\"email\"id=\"input_newsletter_block_\"placeholder=\"Enter your email address\"novalidatedisabled=\"disabled\"\/><\/label><button type=\"submit\"class=\"btn btn--brand\"disabled=\"disabled\"><span>Sign Me Up!<\/span><svg width=\"21\" height=\"14\" viewBox=\"0 0 21 14\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n<path d=\"M13.8523 0.42524L12.9323 1.34521C12.7095 1.56801 12.7132 1.9304 12.9404 2.14865L16.7241 5.7823H0.5625C0.251859 5.7823 0 6.03416 0 6.3448V7.6573C0 7.96794 0.251859 8.2198 0.5625 8.2198H16.7241L12.9405 11.8535C12.7132 12.0717 12.7095 12.4341 12.9323 12.6569L13.8523 13.5769C14.072 13.7965 14.4281 13.7965 14.6478 13.5769L20.8259 7.39879C21.0456 7.17913 21.0456 6.82298 20.8259 6.60327L14.6477 0.42524C14.4281 0.205584 14.0719 0.205584 13.8523 0.42524Z\" fill=\"white\"\/>\n<\/svg>\n<\/button><\/div><\/form><\/div>\n\n\n<h2 id=\"h-do-you-need-portainer-if-you-already-have-docker\" class=\"wp-block-heading\">Do You Need Portainer If You Already Have Docker?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Nope. <a target=\"_blank\" href=\"https:\/\/www.dreamhost.com\/blog\/what-is-docker\/\">Docker<\/a> works fine from the terminal, and plenty of people run a couple of containers for years without ever installing a UI on top.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You want Portainer if:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You&#8217;ll juggle more than two or three Docker containers across different apps.<\/li>\n\n\n\n<li>You&#8217;d rather click &#8220;restart&#8221; than retype a Compose command at 11 pm.&nbsp;<\/li>\n\n\n\n<li>You&#8217;re sharing access with someone who doesn&#8217;t live in a terminal.&nbsp;<\/li>\n\n\n\n<li>You want to deploy stack updates from a Git repo without writing a deploy script.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Skip Portainer if:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You&#8217;re running one or two containers that rarely change.&nbsp;<\/li>\n\n\n\n<li>You&#8217;re already comfortable with Docker Compose, and you\u2019re happy to stay there.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Docker itself is mainstream enough that this isn&#8217;t a fringe decision. In the <a target=\"_blank\" href=\"https:\/\/stackoverflow.co\/company\/press\/archive\/stack-overflow-2025-developer-survey\/\">2025 Stack Overflow Developer Survey<\/a>, 71% of respondents reported using Docker \u2014 the top result among cloud development and infrastructure technologies, after a 17-percentage-point jump from 2024. The Portainer project has more than <a target=\"_blank\" href=\"https:\/\/github.com\/portainer\/portainer\">38,000 stars on GitHub<\/a> as of August 2026.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Other options exist. <a href=\"https:\/\/www.dreamhost.com\/hosting\/vps\/dokploy\/\">Dokploy<\/a>, for example, leans further toward a full self-hosted PaaS, handling apps, databases, and SSL for you. Portainer stays closer to plain Docker: same Compose files, same images, just with a UI on top.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you live in tmux and run one app, close this tab. <em>You&#8217;re fine.<\/em> If you&#8217;re running four self-hosted apps and you&#8217;ve googled, &#8220;What are Docker logs again?&#8221; twice this month, keep reading.<\/p>\n\n\n\n<h2 id=\"h-how-big-a-vps-do-you-need-to-run-docker-and-portainer\" class=\"wp-block-heading\">How Big a VPS Do You Need To Run Docker and Portainer?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Portainer publishes no RAM minimum. A 4 GB <a target=\"_blank\" href=\"https:\/\/www.dreamhost.com\/blog\/beginners-guide-vps\/\">VPS<\/a> may suit a small stack, but size it from the combined requirements of the operating system, Docker daemon, and every container you plan to run.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Portainer&#8217;s own <a target=\"_blank\" href=\"https:\/\/docs.portainer.io\/start\/requirements-and-prerequisites\">requirements page<\/a> says surprisingly little about RAM: the Server ships as a lightweight container, and the stated performance guidance in the docs concerns storage (more on that below). The real RAM math is your workloads \u2014 budget for the operating system, the Docker daemon, and every container you plan to run. Budget disk for logs, too: Docker&#8217;s default json-file log driver performs no log rotation, so a chatty container&#8217;s logs can eat a significant amount of disk over time. <a target=\"_blank\" href=\"https:\/\/docs.docker.com\/engine\/logging\/configure\/\">Docker&#8217;s logging docs<\/a> recommend the local driver instead. Once Docker is installed (step 2 below), create Docker&#8217;s configuration directory and set the local logging driver, then restart Docker: run <strong><code>sudo install -d -m 0755 \/etc\/docker<\/code><\/strong>, then <strong><code>printf '%s\\n' '{ \"log-driver\": \"local\" }' | sudo tee \/etc\/docker\/daemon.json &gt; \/dev\/null<\/code><\/strong>, then <strong><code>sudo systemctl restart docker<\/code><\/strong>. Do it before you deploy long-running apps: the local driver rotates logs by default, and the change only applies to containers created after it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s how that maps to real workloads:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" width=\"1089\" height=\"1445\" data-src=\"https:\/\/www.dreamhost.com\/blog\/wp-content\/uploads\/2026\/08\/vps-size-and-workload-docker-portainer-dreamops-a195c957dc63964ff3f33f0a.webp\" alt=\"Table showing VPS sizing for Docker and Portainer: a single small app stack with Ghost, n8n, Postgres, and a reverse proxy fits DreamHost Stack 4 with 4 GB RAM; larger plans are sized from documented workload requirements\" class=\"wp-image-87229 lazyload\" data-srcset=\"https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/08\/vps-size-and-workload-docker-portainer-dreamops-a195c957dc63964ff3f33f0a.webp 1089w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/08\/vps-size-and-workload-docker-portainer-dreamops-a195c957dc63964ff3f33f0a-226x300.webp 226w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/08\/vps-size-and-workload-docker-portainer-dreamops-a195c957dc63964ff3f33f0a-772x1024.webp 772w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/08\/vps-size-and-workload-docker-portainer-dreamops-a195c957dc63964ff3f33f0a-768x1019.webp 768w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/08\/vps-size-and-workload-docker-portainer-dreamops-a195c957dc63964ff3f33f0a-600x796.webp 600w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/08\/vps-size-and-workload-docker-portainer-dreamops-a195c957dc63964ff3f33f0a-730x969.webp 730w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/08\/vps-size-and-workload-docker-portainer-dreamops-a195c957dc63964ff3f33f0a-784x1040.webp 784w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/08\/vps-size-and-workload-docker-portainer-dreamops-a195c957dc63964ff3f33f0a-877x1164.webp 877w\" data-sizes=\"(max-width: 1089px) 100vw, 1089px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1089px; --smush-placeholder-aspect-ratio: 1089\/1445;\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The worksheet is simple addition: look up the documented RAM requirement for each app you plan to run, add the operating system and the Docker daemon, then leave headroom for usage spikes. Portainer itself does not publish a RAM minimum, so it&#8217;s the apps \u2014 not the UI \u2014 that fill the box.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At DreamHost, our <a href=\"https:\/\/www.dreamhost.com\/hosting\/vps\/\" target=\"_blank\">VPS Hosting<\/a> comes in four RAM-based Stack plans (Stack 4, 8, 16, and 32). The entry-level Stack 4 (2 vCPUs, 4 GB RAM, 75 GB of NVMe SSD, full root access) is a natural fit for a small Docker host. On monthly billing it&#8217;s $8.99\/mo for the first 3 months and auto-renews at $15.99\/mo after that (as of August 2026; the full term plus taxes is charged at checkout \u2014 current numbers are always on the <a href=\"https:\/\/www.dreamhost.com\/hosting\/vps\/docker\/\">Docker + Portainer VPS page<\/a>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re on the fence about whether <a href=\"https:\/\/www.dreamhost.com\/blog\/self-hosting\/\" target=\"_blank\">self-hosting<\/a> is worth the time, Stack 4 is the size to start with: big enough to find out, small enough that you&#8217;re not paying for headroom you won&#8217;t use. If your worksheet outgrows it, you can move up a plan and your environment moves with you \u2014 but note that scaling back down to a smaller Stack isn&#8217;t currently supported, so start small on purpose.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>One note on storage:<\/strong> Portainer&#8217;s data volume holds its own database and configuration, plus a clone of any Git repository you deploy stacks from \u2014 Portainer&#8217;s docs warn that large or multiple repos can consume significant disk. The docs&#8217; stated performance guidance is about storage, not memory: SSD-level performance (\u22483.5 MB\/s, 30,000 IOPS or above, under 10ms write latency) is what they call ideal. Stack 4 includes 75 GB of NVMe SSD storage.<\/p>\n\n\n\n<h2 id=\"h-how-do-you-install-docker-and-portainer-on-a-vps\" class=\"wp-block-heading\">How Do You Install Docker and Portainer on a VPS?<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1600\" height=\"1392\" data-src=\"https:\/\/www.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x.webp\" alt=\"Portainer.io initial setup form for creating administrator account with username\" class=\"wp-image-82012 lazyload\" data-srcset=\"https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x.webp 1600w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-300x261.webp 300w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-1024x891.webp 1024w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-768x668.webp 768w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-1536x1336.webp 1536w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-600x522.webp 600w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-1200x1044.webp 1200w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-730x635.webp 730w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-1460x1270.webp 1460w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-784x682.webp 784w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-1568x1364.webp 1568w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/03-Protainer.io-new-installer_1x-877x763.webp 877w\" data-sizes=\"(max-width: 1600px) 100vw, 1600px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1600px; --smush-placeholder-aspect-ratio: 1600\/1392;\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">It takes seven steps. Download and installation time depends on your VPS and network connection. The commands in steps 2 through 6 are written for Ubuntu and can be pasted in order on a clean server; the one place a command needs a value only you know (your own IP address, in step 4) is called out in the step. (Want to skip the whole list? DreamHost VPS Hosting&#8217;s app library includes a <a href=\"https:\/\/www.dreamhost.com\/hosting\/vps\/docker\/\">Docker + Portainer image<\/a> with both preconfigured.)<\/p>\n\n\n\n<h3 id=\"h-1-pick-a-linux-distribution\" class=\"wp-block-heading\">1. Pick a Linux distribution<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This guide uses Ubuntu 24.04 LTS. Docker Engine supports Ubuntu 22.04, 24.04, and 26.04 LTS (as of August 2026). <a href=\"https:\/\/www.dreamhost.com\/hosting\/vps\/ubuntu\/\">Ubuntu<\/a> is one of the pre-installed OS options on DreamHost VPS Hosting.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Debian and RHEL-family distributions like AlmaLinux work too, but they have their own install paths in Docker&#8217;s docs, and the commands below won&#8217;t transfer as-is. One extra wrinkle on the RHEL family: Portainer&#8217;s install doc assumes SELinux is disabled, and says to pass <strong><code>--privileged<\/code><\/strong> to Docker when deploying Portainer if you need SELinux on. If that sentence didn&#8217;t mean anything to you, use Ubuntu.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Not sure what version of Ubuntu you\u2019re running? Learn <a target=\"_blank\" href=\"https:\/\/www.dreamhost.com\/blog\/check-ubuntu-version\/\">five ways to check<\/a>.<\/p>\n\n\n\n<h3 id=\"h-2-install-docker-from-the-official-repository\" class=\"wp-block-heading\">2. Install Docker from the official repository<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Don&#8217;t <strong><code>apt install docker.io<\/code><\/strong>, and don&#8217;t install Docker via snap either \u2014 Docker&#8217;s <a target=\"_blank\" href=\"https:\/\/docs.docker.com\/engine\/install\/ubuntu\/\">official install docs<\/a> list <strong>docker.io<\/strong> among the unofficial packages to uninstall before installing Docker Engine, and Portainer&#8217;s install doc warns the snap package can cause compatibility issues.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, clear out any conflicting packages. This is the removal command from Docker&#8217;s current install doc, and on a clean server it usually finds nothing to remove \u2014 the doc notes that apt might report you have none of these packages installed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Next, add Docker&#8217;s GPG key (each line below is its own command):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt update\nsudo apt install ca-certificates curl\nsudo install -m 0755 -d \/etc\/apt\/keyrings\nsudo curl -fsSL https:\/\/download.docker.com\/linux\/ubuntu\/gpg -o \/etc\/apt\/keyrings\/docker.asc\nsudo chmod a+r \/etc\/apt\/keyrings\/docker.asc<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then add Docker&#8217;s apt repository. This writes the repository definition to <strong><code>\/etc\/apt\/sources.list.d\/docker.sources<\/code><\/strong> \u2014 the file Docker&#8217;s install doc uses \u2014 with your Ubuntu version and CPU architecture filled in automatically:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo tee \/etc\/apt\/sources.list.d\/docker.sources &gt; \/dev\/null &lt;&lt;EOF\nTypes: deb\nURIs: https:\/\/download.docker.com\/linux\/ubuntu\nSuites: $(. \/etc\/os-release &amp;&amp; echo \"$VERSION_CODENAME\")\nComponents: stable\nArchitectures: $(dpkg --print-architecture)\nSigned-By: \/etc\/apt\/keyrings\/docker.asc\nEOF\nsudo apt update<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, install Docker Engine and the Compose plugin in one go:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin<\/code><\/pre>\n\n\n\n<h3 id=\"h-3-verify-docker-is-running\" class=\"wp-block-heading\">3. Verify Docker is running<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Run <strong><code>sudo docker run hello-world<\/code><\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you don&#8217;t get the &#8220;Hello from Docker!&#8221; message, fix the daemon before layering Portainer on top.<\/p>\n\n\n\n<h3 id=\"h-4-wire-ufw-into-docker-before-you-start-portainer\" class=\"wp-block-heading\">4. Wire UFW into Docker before you start Portainer<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the part most guides put at the end, where it&#8217;s too late: when Docker publishes a port, traffic to it is diverted before it reaches the chains UFW filters, so a published port is reachable from the public internet even when UFW says it&#8217;s denied. (The full explanation is <a href=\"#h-how-do-you-stop-docker-from-bypassing-your-firewall\">below<\/a>.) The moment you start Portainer, its port 9443 will be public \u2014 during the very five-minute window when anyone who reaches it first gets to create the admin account. So fix the firewall <em>first<\/em>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If UFW isn&#8217;t already active, allow SSH and enable it. (If your SSH runs on a custom port, allow that port instead of 22, or this is the step where you lock yourself out.)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ufw allow 22\/tcp\nsudo ufw enable<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then install <a target=\"_blank\" href=\"https:\/\/github.com\/chaifeng\/ufw-docker\">chaifeng\/ufw-docker<\/a>, a third-party tool that appends the needed rules to <strong>\/etc\/ufw\/after.rules<\/strong> so traffic to Docker&#8217;s published ports is filtered through the <strong>DOCKER-USER<\/strong> chain. The commands below pin the project&#8217;s <strong>251123<\/strong> release (November 2025) rather than pulling from the moving master branch, and they use curl, which you installed back in step 2:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo curl -fsSL -o \/usr\/local\/bin\/ufw-docker \\\n  https:\/\/github.com\/chaifeng\/ufw-docker\/raw\/251123\/ufw-docker\nsudo chmod +x \/usr\/local\/bin\/ufw-docker\nsudo ufw-docker install\nsudo ufw reload<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After the reload, published container ports are filtered through UFW. If your routed-traffic policy denies incoming connections and no existing rule allows the port, public connections remain blocked until you allow them explicitly. (The ufw-docker project notes that if the rules don&#8217;t take effect after a UFW restart, a server reboot fixes it.)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now pre-approve <em>your own<\/em> access to the Portainer UI. Replace <strong><code>YOUR_HOME_IP<\/code><\/strong> with the public IP you&#8217;ll be browsing from:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ufw route allow proto tcp from YOUR_HOME_IP to any port 9443<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With this rule in place before Portainer starts, IPv4 traffic to the UI is allowed from your IP and nobody else&#8217;s \u2014 including during the initial-admin window. The rule as written covers IPv4 only. ufw-docker&#8217;s docs say the tool also supports IPv6 networks and updates <strong>\/etc\/ufw\/after6.rules<\/strong> when necessary, but don&#8217;t take that on faith: if your VPS answers on a public IPv6 address, run the outside-in test from the firewall section over IPv6 too before you trust it. The trade-off: most home connections change IP occasionally, so you may need to update the rule later. If that annoys you, the cleaner long-term answer is a VPN in front of the UI (covered in the lock-down section).<\/p>\n\n\n\n<h3 id=\"h-5-create-a-persistent-volume-for-portainer-s-data\" class=\"wp-block-heading\">5. Create a persistent volume for Portainer&#8217;s data<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Next, run <strong><code>sudo docker volume create portainer_data<\/code><\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keeping Portainer&#8217;s database on a Docker volume (not inside the container) means a Portainer upgrade won&#8217;t wipe your config.<\/p>\n\n\n\n<h3 id=\"h-6-run-portainer-community-edition\" class=\"wp-block-heading\">6. Run Portainer Community Edition<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the command from <a target=\"_blank\" href=\"https:\/\/docs.portainer.io\/start\/install-ce\/server\/docker\/linux\">Portainer&#8217;s Linux install doc<\/a>, minus one flag: the official version also publishes port 8000, a TCP tunnel that&#8217;s only required if you later connect remote environments via Edge Agents. A single-host install doesn&#8217;t need it, so we leave it out:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo docker run -d \\\n  -p 9443:9443 \\\n  --name portainer \\\n  --restart=always \\\n  -v \/var\/run\/docker.sock:\/var\/run\/docker.sock \\\n  -v portainer_data:\/data \\\n  portainer\/portainer-ce:lts<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s break that down line by line:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>-d<\/code> <\/strong>runs it detached.<\/li>\n\n\n\n<li><strong><code>-p 9443:9443<\/code><\/strong> is the Portainer web UI on HTTPS \u2014 the port you allowed for your IP in step 4.<\/li>\n\n\n\n<li><strong><code>--restart=always<\/code><\/strong> brings Portainer back up after a reboot.<\/li>\n\n\n\n<li><strong><code>-v<\/code><\/strong> flags are the socket bind and the persistent volume from step 5. <\/li>\n\n\n\n<li><strong><code>:lts<\/code><\/strong> pins you to Portainer&#8217;s Long Term Support release line \u2014 the tag Portainer&#8217;s own install doc uses.<\/li>\n<\/ul>\n\n\n\n<h3 id=\"h-7-open-the-ui-and-create-your-admin-account\" class=\"wp-block-heading\">7. Open the UI and create your admin account<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Point your browser to<strong> https:\/\/YOUR_VPS_IP:9443<\/strong> (from the IP address you allowed in step 4).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Click through the self-signed cert warning. You&#8217;ll land on the &#8220;create the initial administrator user&#8221; screen. Pick a username that isn&#8217;t admin, set a strong password, and save it somewhere real. And\u2026 you&#8217;re in!<\/p>\n\n\n\n<h2 id=\"h-why-did-portainer-just-time-out-on-me\" class=\"wp-block-heading\">Why Did Portainer Just Time Out on Me?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Because Portainer waits exactly five minutes for someone to claim it on first install. If nobody does, it locks the door.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Verbatim from <a target=\"_blank\" href=\"https:\/\/docs.portainer.io\/faqs\/installing\/i-just-installed-portainer-but-i-cant-access-the-ui-how-do-i-fix-this\">Portainer&#8217;s own FAQ<\/a>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&#8220;<em>As a security precaution, when Portainer is first installed it will wait for 5 minutes for an administrator user to be created (part of the initial setup). If a user is not created within those 5 minutes, the Portainer Server will stop listening for requests.<\/em>&#8220;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Translation: if you spent Step 6 making a sandwich, your install is now refusing to talk to you \u2014 and you didn&#8217;t do anything wrong.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The fix is simple. Run <strong><code>sudo docker restart portainer<\/code><\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That spins the container back up, gives you another five-minute window, and drops you on the create-admin screen. Repeat as needed. Welcome to <a target=\"_blank\" href=\"https:\/\/www.dreamhost.com\/blog\/self-hosting\/\">self-hosting<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re scripting the install for a fleet (Ansible, Terraform, a deploy pipeline), Portainer accepts an <strong><code>--admin-password-file<\/code><\/strong> flag that creates the admin user from a file when the container first starts \u2014 so there&#8217;s no create-admin screen to race to. Portainer&#8217;s <a href=\"https:\/\/docs.portainer.io\/advanced\/cli\" target=\"_blank\">CLI configuration doc<\/a> covers the syntax. For a one-off VPS install, the restart trick is fine.<\/p>\n\n\n\n<h2 id=\"h-how-do-you-stop-docker-from-bypassing-your-firewall\" class=\"wp-block-heading\">How Do You Stop Docker From Bypassing Your Firewall?<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1600\" height=\"1229\" data-src=\"https:\/\/www.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall.webp\" alt=\"Docker firewall comparison: without DOCKER-USER rule allows unrestricted container access\" class=\"wp-image-82013 lazyload\" data-srcset=\"https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall.webp 1600w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-300x230.webp 300w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-1024x787.webp 1024w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-768x590.webp 768w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-1536x1180.webp 1536w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-600x461.webp 600w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-1200x922.webp 1200w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-730x561.webp 730w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-1460x1121.webp 1460w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-784x602.webp 784w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-1568x1204.webp 1568w, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/04-Docker-Firewall-877x674.webp 877w\" data-sizes=\"(max-width: 1600px) 100vw, 1600px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1600px; --smush-placeholder-aspect-ratio: 1600\/1229;\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You already did, in step 4. Here&#8217;s what actually happened, how to open ports on purpose, and how to prove it worked.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The problem: when you publish a container&#8217;s port with <strong><code>-p 80:80<\/code><\/strong>, that port is reachable from the public internet even if <strong>ufw status <\/strong>shows port 80 as denied. Per <a target=\"_blank\" href=\"https:\/\/docs.docker.com\/engine\/network\/packet-filtering-firewalls\/\">Docker&#8217;s packet-filtering documentation<\/a>, &#8220;Docker routes container traffic in the nat table, which means that packets are diverted before it reaches the INPUT and OUTPUT chains that ufw uses.&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">UFW isn&#8217;t broken. It&#8217;s just standing in the wrong hallway.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The fix Docker itself points to is the <a target=\"_blank\" href=\"https:\/\/docs.docker.com\/engine\/network\/firewall-iptables\/\"><strong>DOCKER-USER<\/strong> iptables chain<\/a> \u2014 a chain Docker creates specifically for user-defined rules, processed before the rules in Docker&#8217;s own chains. The <strong>ufw-docker<\/strong> script you installed in step 4 wires UFW into that chain by appending a rule block (between <strong># BEGIN UFW AND DOCKER<\/strong> and <strong># END UFW AND DOCKER<\/strong> markers) to <strong>\/etc\/ufw\/after.rules<\/strong>, so it loads with the rest of your UFW configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>To open a container port to the public on purpose<\/strong> \u2014 say, a web app on container port 80 \u2014 use a route rule:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ufw route allow proto tcp from any to any port 80<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">One trap the ufw-docker docs call out: the rule takes the <em>container<\/em> port, not the host port. If you published with <strong><code>-p 8080:80<\/code><\/strong>, the rule above (port 80) is the right one, not port 8080.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>To verify from the outside<\/strong>, test from a network that isn&#8217;t on your allow list \u2014 a phone hotspot works. This should time out rather than return Portainer&#8217;s login page:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -k --max-time 5 https:\/\/YOUR_VPS_IP:9443\n\n# For IPv6, enclose the address in square brackets:\ncurl -k --max-time 5 'https:\/\/[YOUR_VPS_IPV6]:9443'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run the same command from the IP you allowed in step 4 and you should get HTML back. UFW saying &#8220;denied&#8221; is a claim; a timeout from a strange network is evidence.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>To undo it all<\/strong>, run <strong><code>sudo ufw-docker uninstall<\/code><\/strong>, which the project documents as restoring your UFW configuration and deleting the files it installed, then <strong><code>sudo ufw reload<\/code><\/strong>. You can also remove the block between the <strong># BEGIN UFW AND DOCKER<\/strong> and <strong># END UFW AND DOCKER<\/strong> markers in <strong>\/etc\/ufw\/after.rules<\/strong> by hand and reload UFW.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This one catches sharp engineers on personal boxes and production servers alike: UFW says one thing, the public internet says another, and the truth is in iptables. If you&#8217;d rather Portainer never had a public port at all, <a target=\"_blank\" href=\"https:\/\/www.portainer.io\/how-to\/how-to-secure-your-portainer-installation\">front it with a VPN tunnel<\/a> like WireGuard.<\/p>\n\n\n\n<h2 id=\"h-what-should-you-lock-down-right-after-the-first-login\" class=\"wp-block-heading\">What Should You Lock Down Right After the First Login?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You\u2019re in. Five things worth doing before you close that browser tab \u2014 the first four come straight from Portainer&#8217;s own <a target=\"_blank\" href=\"https:\/\/www.portainer.io\/how-to\/how-to-secure-your-portainer-installation\">security guide<\/a>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Turn on Force HTTPS only.<\/strong> The install in this guide publishes only port 9443, so no HTTP port is exposed in the first place. Flipping the <em>Force HTTPS only<\/em> toggle under <em>Settings \u2192 SSL Certificate<\/em> locks that in: per <a target=\"_blank\" href=\"https:\/\/docs.portainer.io\/admin\/settings\/general\">Portainer&#8217;s settings docs<\/a>, it disables listening on the legacy HTTP port (9000) entirely, so a future redeploy that adds <strong><code>-p 9000:9000<\/code><\/strong> can&#8217;t reopen an unencrypted path.<\/li>\n\n\n\n<li><strong>Replace the self-signed certificate.<\/strong> Upload a cert you generated, or front Portainer with an NGINX reverse proxy holding a Let&#8217;s Encrypt certificate. Past day one, clicking through the browser warning every time is a habit you&#8217;ll regret.<\/li>\n\n\n\n<li><strong>Use a real admin username.<\/strong> Using the default admin username gives an attacker one less credential to determine. Pick something specific to you.<\/li>\n\n\n\n<li><strong>Restrict the UI to a VPN.<\/strong> Running Portainer behind a WireGuard VPN is the pattern Portainer&#8217;s security guide points to. A 9443 restricted to your source IP (step 4) is good. A 9443 that&#8217;s only reachable on a private network is better.<\/li>\n\n\n\n<li><strong>Audit the Docker socket bind.<\/strong> Anything with access to <strong><code>\/var\/run\/docker.sock<\/code><\/strong> controls the Docker daemon, and through it, the host. <em>Do not<\/em> mount the socket casually into other containers. Every additional bind is a new path to the host. If a container claims it needs the socket, ask why \u2014 and consider a socket proxy if the answer is &#8220;for monitoring.&#8221;<\/li>\n<\/ul>\n\n\n\n<h2 id=\"h-how-do-you-actually-use-portainer-day-to-day\" class=\"wp-block-heading\">How Do You Actually Use Portainer Day-to-Day?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The payoff for installing Portainer is the day you stop SSH&#8217;ing in to restart a misbehaving container, and start clicking a button instead. The bigger payoff is stack management.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A Portainer stack is a multi-container app defined by a Docker Compose file. Portainer can deploy supported Docker Compose configurations as stacks through four routes listed in the <a target=\"_blank\" href=\"https:\/\/docs.portainer.io\/user\/docker\/stacks\/add\">official stacks docs<\/a>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Paste Compose into the web editor<\/li>\n\n\n\n<li>Upload a Compose file<\/li>\n\n\n\n<li>Point Portainer at a Git repository<\/li>\n\n\n\n<li>Deploy from a custom template<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"2560\" height=\"1181\" data-src=\"https:\/\/www.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/05-How-Do-You-Actually-Use-Portainer-Day-to-Day_-scaled.gif\" alt=\"Portainer.io dashboard showing Stack Details with containers table, logs, stats\" class=\"wp-image-82014 lazyload\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 2560px; --smush-placeholder-aspect-ratio: 2560\/1181;\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The Git path is the one that matters. You point Portainer at a public or authenticated repo and a path to <strong>docker-compose.yml<\/strong>, then toggle on GitOps updates. Portainer either polls the repo on a fetch interval you set, or gives you a webhook URL to add to your Git host (triggered from a GitHub Action, for example). When the Compose file changes upstream, Portainer pulls and redeploys automatically. And yes, this works in Community Edition \u2014 on the stacks docs page, only the relative-path volumes option carries the Business Edition flag.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the self-hosted version of &#8220;deploy on push&#8221; without paying a PaaS for the privilege.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The smaller daily wins add up too. Think logs in a panel instead of following them from the command line, one-click restarts, rebuilding a stack from a button, and resource graphs you can scan in three seconds instead of running Docker stats.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you later add remote hosts through Edge Agents, port 8000 comes back into play \u2014 the <a href=\"#h-can-portainer-manage-multiple-docker-hosts\">FAQ below<\/a> covers the redeploy and the firewall rule.<\/p>\n\n\n\n<h2 id=\"h-how-do-you-back-up-portainer-and-your-containers\" class=\"wp-block-heading\">How Do You Back Up Portainer and Your Containers?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A volume is persistence, not a backup. The <strong>portainer_data<\/strong> volume from step 5 survives container upgrades, but it lives on the same disk as everything else \u2014 so a real backup plan has three parts, and all three leave the VPS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. Portainer&#8217;s own configuration.<\/strong> In the UI, go to <em>Settings \u2192 Back up Portainer<\/em>, keep the default <em>Download backup file<\/em> option, and click <em>Download backup<\/em>. Per <a target=\"_blank\" href=\"https:\/\/docs.portainer.io\/admin\/settings\/general\">Portainer&#8217;s settings docs<\/a>, that downloads a tar.gz of everything Portainer stores on its \/data volume, optionally password-encrypted, and that archive is all you need to restore Portainer. The docs are equally clear about scope: it backs up <em>only<\/em> the Portainer configuration, not the containers, stacks, or volumes you&#8217;ve deployed. (Scheduled backups to S3 exist, but only in Business Edition \u2014 in CE, put a recurring reminder on your calendar.)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. Your application data.<\/strong> Docker&#8217;s <a target=\"_blank\" href=\"https:\/\/docs.docker.com\/engine\/storage\/volumes\/\">volumes documentation<\/a> calls named volumes the preferred way to persist container data, and easier to back up or migrate than bind mounts. A read-only mount only stops the <em>backup<\/em> container from writing \u2014 the app itself can still write to the volume mid-archive and hand you an inconsistent copy. So stop the app&#8217;s container first (<strong><code>sudo docker stop myapp<\/code><\/strong>), or use the app&#8217;s own supported backup mechanism. Then mount the volume read-only into a throwaway container and tar it up, and start the app again after (swap in your own names):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo docker run --rm \\\n  -v myapp_data:\/source:ro \\\n  -v \"$PWD\":\/backup \\\n  alpine tar czf \/backup\/myapp_data.tgz -C \/source .<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For databases, prefer the database&#8217;s own dump tool (pg_dump, mysqldump) over copying live data files, so you&#8217;re not archiving a file mid-write. Then ship the archives off the box on a schedule \u2014 rsync or scp to another machine, or push to object storage. A backup that lives on the disk it&#8217;s backing up disappears with that disk.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>3. Your Compose files and secrets.<\/strong> If you deploy stacks from Git, the Compose files are already off-host. What Git does <em>not<\/em> hold is anything you never committed: .env files, API keys, and passwords you supplied at deploy time. Bind-mounted directories also sit outside the volume backup above. Inventory those separately and back them up encrypted.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Then test the restore, before you need it.<\/strong> Portainer&#8217;s docs say a configuration restore only works on a fresh instance: deploy a new Portainer container with an empty data volume (a throwaway VPS or your laptop is fine), choose <em>Restore Portainer from backup<\/em> during the initial setup, and confirm you can log in with your old credentials and see your stack definitions. That drill covers part 1 only \u2014 Portainer&#8217;s configuration. Parts 2 and 3 need their own test, per app: restore an archive into a fresh volume, start the app against it, and check the data is actually there. An untested backup is a hope with a filename.<\/p>\n\n\n\n<h2 id=\"h-what-does-portainer-cost-and-when-does-free-stop-being-free\" class=\"wp-block-heading\">What Does Portainer Cost? (And When Does Free Stop Being Free?)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Portainer is free twice over. Community Edition is free and <a target=\"_blank\" href=\"https:\/\/www.dreamhost.com\/blog\/open-source-vs-proprietary\/\">open source<\/a> (zlib license), with no license key required. And Portainer Business Edition is <a target=\"_blank\" href=\"https:\/\/www.portainer.io\/take-3\">free forever for up to 3 nodes<\/a>: full Business features, no time limit, no credit card. You register for a free license key instead of paying.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><strong>Edition<\/strong><\/th><th><strong>Cost<\/strong><\/th><th><strong>Nodes<\/strong><\/th><th><strong>Best for<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Community Edition<\/td><td>Free, open-source<\/td><td>No node licensing<\/td><td>Personal projects, homelab, single-VPS self-hosters<\/td><\/tr><tr><td>Business Edition \u2014 3 Nodes Free<\/td><td>Free, no time limit<\/td><td>Up to 3<\/td><td>Small homelabs that want Business features like RBAC<\/td><\/tr><tr><td>Home &amp; Student<\/td><td>$155\/yr<\/td><td>Up to 15<\/td><td>Bigger homelabs and students; strictly non-commercial<\/td><\/tr><tr><td>Business Starter<\/td><td>From $105\/mo<\/td><td>5, 10, or 15<\/td><td>Small teams managing real infrastructure (community support)<\/td><\/tr><tr><td>Business Scale<\/td><td>From $209\/mo<\/td><td>5 to 35<\/td><td>Growing teams needing 9&#215;5 support<\/td><\/tr><tr><td>Enterprise<\/td><td>Quote<\/td><td>Custom<\/td><td>Compliance and priority-support needs<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Prices are from <a target=\"_blank\" href=\"https:\/\/www.portainer.io\/pricing\">Portainer&#8217;s pricing page<\/a> as of August 2026; Starter and Scale require an annual commitment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So which should you pick? For a single VPS with one admin, <strong>Community Edition<\/strong>: it&#8217;s open source, needs no registration, and does everything in this guide, Git deploys included. If you want role-based access control (RBAC) for a second user, or you&#8217;re already running two or three boxes, take the Business 3-nodes-free tier instead \u2014 it&#8217;s the same software with the Business features unlocked, free for as long as you stay at three nodes or fewer. The paid plans only make sense when you&#8217;re managing real node counts across a team \u2014 and check the support line before assuming paid means supported: as of August 2026, Portainer&#8217;s pricing page lists community support on the Starter plan, 9&#215;5 next-business-day support from the Scale plan up, and prioritized support on Enterprise.<\/p>\n\n\n\n<h2 id=\"h-should-you-add-portainer-or-skip-it\" class=\"wp-block-heading\">Should You Add Portainer or Skip It?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s the honest two-sentence answer:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 If you\u2019re going to share this server, deploy from Git, or run more than three containers a year from now, install Portainer while the host is fresh and your habits are still forming around it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u274c If you\u2019re running one quiet app and you\u2019re happy in the terminal, you won\u2019t miss the UI you didn\u2019t install.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The real question isn\u2019t Portainer vs. no Portainer. It\u2019s whether self-hosting is the right call for you at all \u2014 and you answered that when you opened this tab.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A 4 GB VPS, Docker, and Portainer CE are enough to start self-hosting for real: pick your apps, sum their documented requirements, and let the worksheet \u2014 not the wishlist \u2014 decide when the box needs to grow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That\u2019s not a bad deal.<\/p>\n\n\n\n<h2 id=\"h-frequently-asked-questions-about-docker-and-portainer\" class=\"wp-block-heading\">Frequently Asked Questions About Docker and Portainer<\/h2>\n\n\n\n<h3 id=\"h-can-you-run-docker-on-a-vps\" class=\"wp-block-heading\">Can you run Docker on a VPS?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. A VPS with root access is one of the most flexible places to run Docker: you install Docker Engine on the server&#8217;s Linux OS, and each app runs in its own container. The distinction people trip on is that the VPS is the rented virtual server, while Docker is the software you run on it to isolate applications into containers. Any VPS with root access and enough RAM for your containers works \u2014 <a target=\"_blank\" href=\"https:\/\/www.dreamhost.com\/hosting\/vps\/docker\/\">DreamHost VPS Hosting<\/a> even offers a preinstalled Docker + Portainer image.<\/p>\n\n\n\n<h3 id=\"h-what-is-portainer-used-for\" class=\"wp-block-heading\">What is Portainer used for?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Portainer is used for managing Docker, Docker Swarm, and Kubernetes containers through a web interface. It runs as a Docker container itself, connects to the host&#8217;s Docker socket, and gives you a browser view and control surface over every other container on the host \u2014 starting and stopping containers, deploying multi-container stacks, viewing logs, and managing user access without SSH&#8217;ing in.<\/p>\n\n\n\n<h3 id=\"h-do-i-need-portainer-if-i-have-docker\" class=\"wp-block-heading\">Do I need Portainer if I have Docker?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No, you don&#8217;t need Portainer to use Docker. Docker works completely from the terminal, and many self-hosters never install a UI on top.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Portainer earns its keep when you&#8217;re juggling more than two or three containers, sharing access with someone who doesn&#8217;t live in a terminal, or deploying stack updates from a Git repository. Skip it if you&#8217;re running one or two stable containers and you&#8217;re comfortable with Docker Compose.<\/p>\n\n\n\n<h3 id=\"h-what-port-does-portainer-run-on\" class=\"wp-block-heading\">What port does Portainer run on?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Portainer serves its web UI over HTTPS on port 9443 by default. The official install command in <a href=\"https:\/\/docs.portainer.io\/start\/install-ce\/server\/docker\/linux\" target=\"_blank\">Portainer&#8217;s Linux install doc<\/a> also publishes port 8000, a TCP tunnel used only by Edge Agents when you connect remote Docker hosts later \u2014 it&#8217;s optional, which is why the single-host install in this guide drops the <strong><code>-p 8000:8000<\/code><\/strong> flag entirely.<\/p>\n\n\n\n<h3 id=\"h-how-much-ram-does-portainer-need\" class=\"wp-block-heading\">How much RAM does Portainer need?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Portainer&#8217;s current system requirements don&#8217;t publish a RAM minimum \u2014 the Server runs as a lightweight container, and the docs&#8217; stated performance guidance is about disk, not memory. Size your RAM by summing the documented requirements of the operating system, the Docker daemon, and every app you&#8217;ll run, then leave headroom for spikes.<br><br>At DreamHost, our entry-level <a target=\"_blank\" href=\"https:\/\/www.dreamhost.com\/hosting\/vps\/\">VPS Hosting<\/a> plan, Stack 4, comes with 4 GB of RAM \u2014 a starting point for a small stack of side-project containers like <a target=\"_blank\" href=\"https:\/\/www.dreamhost.com\/blog\/ghost-self-hosted\/\">Ghost<\/a> or <a target=\"_blank\" href=\"https:\/\/www.dreamhost.com\/blog\/n8n-self-hosted\/\">n8n<\/a>. Whether 4 GB is enough depends on the apps you actually run; if the math outgrows it, you can move up to a bigger Stack plan.<\/p>\n\n\n\n<h3 id=\"h-is-portainer-free\" class=\"wp-block-heading\">Is Portainer free?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes, twice over. Portainer Community Edition is free and <a target=\"_blank\" href=\"https:\/\/www.dreamhost.com\/blog\/open-source-vs-proprietary\/\">open-source<\/a>, with no license key required. Portainer Business Edition (which adds features like role-based access control) is also free for up to 3 nodes, with full features and no time limit; you register for a free license key instead of paying. Above three nodes, Business pricing starts at $105\/mo on the Starter plan per <a target=\"_blank\" href=\"https:\/\/www.portainer.io\/pricing\">Portainer&#8217;s pricing page<\/a> (as of August 2026), and a $155\/year Home &amp; Student license covers non-commercial homelabs up to 15 nodes.<\/p>\n\n\n\n<h3 id=\"h-can-portainer-manage-multiple-docker-hosts\" class=\"wp-block-heading\">Can Portainer manage multiple Docker hosts?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes, Portainer manages multiple Docker hosts through Agents and Edge Agents. The standard Agent listens on port 9001 on each remote host; Edge Agents instead connect out to the Portainer Server&#8217;s tunnel port 8000, and per Portainer&#8217;s requirements docs they need no open inbound ports of their own. For a single-VPS self-hoster, none of that machinery is needed \u2014 which is why this guide&#8217;s install drops the port 8000 flag that Portainer&#8217;s official command includes. The day you connect a remote environment through an Edge Agent, redeploy Portainer with <strong><code>-p 8000:8000<\/code><\/strong> added, then allow container port 8000 through UFW from each Edge Agent&#8217;s public egress IP, replacing <strong><code>EDGE_AGENT_IP<\/code><\/strong> here: <strong><code>sudo ufw route allow proto tcp from EDGE_AGENT_IP to any port 8000<\/code><\/strong>.<\/p>\n\n\n\n<h3 id=\"h-does-docker-bypass-ufw-on-a-vps\" class=\"wp-block-heading\">Does Docker bypass UFW on a VPS?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes, Docker bypasses UFW by default because Docker manages container traffic at the iptables NAT layer, which sits in front of UFW&#8217;s INPUT and OUTPUT chains.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Per <a target=\"_blank\" href=\"https:\/\/docs.docker.com\/engine\/network\/packet-filtering-firewalls\/\">Docker&#8217;s packet-filtering documentation<\/a>, &#8220;packets are diverted before it reaches the INPUT and OUTPUT chains that ufw uses.&#8221; The fix is to add your rules to the <a target=\"_blank\" href=\"https:\/\/docs.docker.com\/engine\/network\/firewall-iptables\/\"><strong>DOCKER-USER<\/strong> iptables chain<\/a>, and a community tool for wiring UFW into it is <a target=\"_blank\" href=\"https:\/\/github.com\/chaifeng\/ufw-docker\">chaifeng\/ufw-docker<\/a> \u2014 installed before Portainer ever starts, in step 4 of this guide.<\/p>\n\n\n\n\n<div class=\"article-cta-shared article-cta-small article-cta--product\">\n\t<div class=\"tr-img-wrap-outer jsLoading\"><img decoding=\"async\" class=\"js-img-lazy \" src=\"https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/themes\/blog2018\/assets\/img\/lazy-loading-transparent.webp\" data-srcset=\"https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/cta_image_a-877x522.webp 1x, https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/cta_image_a.webp 2x\"  \/><\/div>\n\n\t<a href='https:\/\/www.dreamhost.com\/hosting\/vps\/' class='link-top' target='_blank' rel='noopener noreferrer'>\n\t\t<span>VPS<\/span>\n\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 384 512\" width=\"15\"><path d=\"M342.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L274.7 256 105.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z\"\/><\/svg>\n\t<\/a>\n\n\t<div class=\"content-btm\">\n\t\t<h2 class=\"h2--md\">\n\t\t\tOwn Your Entire Stack. Apps, AI, Databases, and More.\n\t\t<\/h2>\n\t\t<p class=\"p--md\">\n\t\t\tKeep every credential and conversation on a server you control, with NVMe speed and unmetered bandwidth built in.\n\t\t<\/p>\n\n\t\t        <a\n            href=\"https:\/\/www.dreamhost.com\/hosting\/vps\/\"\n                        class=\"btn btn--white-outline btn--sm btn--round\"\n                                    target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            >\n                            Explore VPS Hosting Plans                    <\/a>\n\n\t<\/div>\n<\/div>\n\n\n<script type=\"application\/ld+json\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"FAQPage\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"Can you run Docker on a VPS?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes. A VPS with root access is one of the most flexible places to run Docker: you install Docker Engine on the server's Linux OS, and each app runs in its own container. The VPS is the rented virtual server; Docker is the software you run on it to isolate applications into containers.\"}},{\"@type\":\"Question\",\"name\":\"What is Portainer used for?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Portainer is used for managing Docker, Docker Swarm, and Kubernetes containers through a web interface. It runs as a Docker container itself, connects to the host's Docker socket, and gives you a browser view and control surface over every other container on the host \u2014 starting and stopping containers, deploying multi-container stacks, viewing logs, and managing user access without SSH'ing in.\"}},{\"@type\":\"Question\",\"name\":\"Do I need Portainer if I have Docker?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"No, you don't need Portainer to use Docker. Docker works completely from the terminal, and many self-hosters never install a UI on top.\"}},{\"@type\":\"Question\",\"name\":\"What port does Portainer run on?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Portainer serves its web UI over HTTPS on port 9443 by default. The official install command also publishes port 8000, a TCP tunnel used only by Edge Agents when you connect remote Docker hosts later \u2014 it's optional on a single-host install.\"}},{\"@type\":\"Question\",\"name\":\"How much RAM does Portainer need?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Portainer's current system requirements don't publish a RAM minimum \u2014 the Server runs as a lightweight container, and the docs' stated performance guidance is about disk, not memory. Size your RAM by summing the documented requirements of the operating system, the Docker daemon, and every app you'll run, then leave headroom for spikes.\"}},{\"@type\":\"Question\",\"name\":\"Is Portainer free?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes, twice over. Portainer Community Edition is free and open-source, with no license key required. Portainer Business Edition is also free for up to 3 nodes, with full features and no time limit; above three nodes, Business pricing starts at $105\/mo on the Starter plan as of August 2026.\"}},{\"@type\":\"Question\",\"name\":\"Can Portainer manage multiple Docker hosts?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes, Portainer manages multiple Docker hosts through Agents and Edge Agents. The standard Agent listens on port 9001 on each remote host; Edge Agents instead connect out to the Portainer Server's tunnel port 8000 and need no open inbound ports of their own.\"}},{\"@type\":\"Question\",\"name\":\"Does Docker bypass UFW on a VPS?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes, Docker bypasses UFW by default because Docker manages container traffic at the iptables NAT layer, which sits in front of UFW's INPUT and OUTPUT chains. The fix is to add rules to the DOCKER-USER iptables chain; a community tool for wiring UFW into it is chaifeng\/ufw-docker.\"}}]},{\"@type\":\"HowTo\",\"name\":\"How to Install Docker and Portainer on a VPS\",\"description\":\"Install Docker and Portainer Community Edition on an Ubuntu VPS in seven steps, from picking a Linux distribution to creating the admin account. Download and installation time depends on your VPS and network connection.\",\"step\":[{\"@type\":\"HowToStep\",\"position\":1,\"name\":\"Pick a Linux distribution\",\"text\":\"This guide uses Ubuntu 24.04 LTS. Docker Engine supports Ubuntu 22.04, 24.04, and 26.04 LTS (as of August 2026). Debian and RHEL-family distributions like AlmaLinux have their own install paths in Docker's docs, and Portainer's install doc assumes SELinux is disabled on RHEL-family systems.\"},{\"@type\":\"HowToStep\",\"position\":2,\"name\":\"Install Docker from the official repository\",\"text\":\"Don't apt install docker.io \u2014 Docker's own install doc lists docker.io among the unofficial packages to uninstall first. Remove conflicting packages, add Docker's GPG key and apt repository per the official Docker install docs, then run: sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\"},{\"@type\":\"HowToStep\",\"position\":3,\"name\":\"Verify Docker is running\",\"text\":\"Run sudo docker run hello-world. If you don't get the \\\"Hello from Docker!\\\" message, fix the daemon before layering Portainer on top.\"},{\"@type\":\"HowToStep\",\"position\":4,\"name\":\"Wire UFW into Docker before you start Portainer\",\"text\":\"Docker-published ports bypass UFW by default. Enable UFW (allowing SSH first), install the chaifeng\/ufw-docker script, run ufw-docker install, reload UFW, and add a source-IP allow rule for container port 9443 \u2014 sudo ufw route allow proto tcp from YOUR_HOME_IP to any port 9443 \u2014 before Portainer starts, so the UI is never publicly reachable during its five-minute initial-admin window.\"},{\"@type\":\"HowToStep\",\"position\":5,\"name\":\"Create a persistent volume for Portainer's data\",\"text\":\"Run sudo docker volume create portainer_data. Keeping Portainer's database on a Docker volume (not inside the container) means a Portainer upgrade won't wipe your config.\"},{\"@type\":\"HowToStep\",\"position\":6,\"name\":\"Run Portainer Community Edition\",\"text\":\"Use the official install command from Portainer's Linux install doc, minus the optional Edge Agent tunnel port: sudo 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. The -d flag runs it detached, -p 9443:9443 is the Portainer web UI on HTTPS, --restart=always brings Portainer back up after a reboot, the -v flags are the socket bind and the persistent volume from step 5, and the :lts tag is Portainer's Long Term Support release line.\"},{\"@type\":\"HowToStep\",\"position\":7,\"name\":\"Open the Portainer UI and create the admin account\",\"text\":\"Point your browser to https:\/\/YOUR_VPS_IP:9443 from the IP address you allowed in step 4. Click through the self-signed cert warning. You'll land on the \\\"create the initial administrator user\\\" screen. Pick a username that isn't admin, set a strong password, and save it somewhere real.\"}]}]}<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Run Docker on a VPS with Portainer&#8217;s web UI in front. Install steps, sizing, security, and the gotchas every install guide skips. Roughly 30 minutes start to finish.<\/p>\n","protected":false},"author":2,"featured_media":82008,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_yoast_wpseo_opengraph-title":"How To Run Docker on a VPS With Portainer (2026)","_yoast_wpseo_opengraph-description":"Install Docker and Portainer CE on an Ubuntu VPS in 7 steps \u2014 firewall first, then the one-command install, sizing math, backups, and real costs.","_yoast_wpseo_twitter-title":"How To Run Docker on a VPS With Portainer (2026)","_yoast_wpseo_twitter-description":"Install Docker and Portainer CE on an Ubuntu VPS in 7 steps \u2014 firewall first, then the one-command install, sizing math, backups, and real costs.","toc_headlines":"[[\"h-what-is-portainer-exactly\",\"What Is Portainer, Exactly?\"],[\"h-do-you-need-portainer-if-you-already-have-docker\",\"Do You Need Portainer If You Already Have Docker?\"],[\"h-how-big-a-vps-do-you-need-to-run-docker-and-portainer\",\"How Big a VPS Do You Need To Run Docker and Portainer?\"],[\"h-how-do-you-install-docker-and-portainer-on-a-vps\",\"How Do You Install Docker and Portainer on a VPS?\"],[\"h-why-did-portainer-just-time-out-on-me\",\"Why Did Portainer Just Time Out on Me?\"],[\"h-how-do-you-stop-docker-from-bypassing-your-firewall\",\"How Do You Stop Docker From Bypassing Your Firewall?\"],[\"h-what-should-you-lock-down-right-after-the-first-login\",\"What Should You Lock Down Right After the First Login?\"],[\"h-how-do-you-actually-use-portainer-day-to-day\",\"How Do You Actually Use Portainer Day-to-Day?\"],[\"h-how-do-you-back-up-portainer-and-your-containers\",\"How Do You Back Up Portainer and Your Containers?\"],[\"h-what-does-portainer-cost-and-when-does-free-stop-being-free\",\"What Does Portainer Cost? (And When Does Free Stop Being Free?)\"],[\"h-should-you-add-portainer-or-skip-it\",\"Should You Add Portainer or Skip It?\"],[\"h-frequently-asked-questions-about-docker-and-portainer\",\"Frequently Asked Questions About Docker and Portainer\"]]","hide_toc":false,"show_updated_at":"1","footnotes":""},"categories":[550,807,804],"tags":[],"class_list":["post-82006","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","category-vps-hosting","category-web-hosting"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.3 (Yoast SEO v28.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>How To Run Docker on a VPS With Portainer (2026)<\/title>\n<meta name=\"description\" content=\"Install Docker and Portainer CE on an Ubuntu VPS in 7 steps \u2014 firewall first, then the one-command install, sizing math, backups, and real costs.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How To Run Docker on a VPS With Portainer (2026)\" \/>\n<meta property=\"og:description\" content=\"Install Docker and Portainer CE on an Ubuntu VPS in 7 steps \u2014 firewall first, then the one-command install, sizing math, backups, and real costs.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/\" \/>\n<meta property=\"og:site_name\" content=\"DreamHost Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/DreamHost\/\" \/>\n<meta property=\"article:published_time\" content=\"2026-06-19T14:30:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-26T03:55:49+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/1220-x-628-OGIMAGE_Run-Docker-and-Portainer-on-a-VPS-2026-Guide_1x.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"628\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"author\" content=\"Dallas Kashuba\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:title\" content=\"How To Run Docker on a VPS With Portainer (2026)\" \/>\n<meta name=\"twitter:description\" content=\"Install Docker and Portainer CE on an Ubuntu VPS in 7 steps \u2014 firewall first, then the one-command install, sizing math, backups, and real costs.\" \/>\n<meta name=\"twitter:creator\" content=\"@http:\/\/twitter.com\/dallas\" \/>\n<meta name=\"twitter:site\" content=\"@dreamhost\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Dallas Kashuba\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"15 minutes\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How To Run Docker on a VPS With Portainer (2026)","description":"Install Docker and Portainer CE on an Ubuntu VPS in 7 steps \u2014 firewall first, then the one-command install, sizing math, backups, and real costs.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/","og_locale":"en_US","og_type":"article","og_title":"How To Run Docker on a VPS With Portainer (2026)","og_description":"Install Docker and Portainer CE on an Ubuntu VPS in 7 steps \u2014 firewall first, then the one-command install, sizing math, backups, and real costs.","og_url":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/","og_site_name":"DreamHost Blog","article_publisher":"https:\/\/www.facebook.com\/DreamHost\/","article_published_time":"2026-06-19T14:30:00+00:00","article_modified_time":"2026-08-26T03:55:49+00:00","og_image":[{"width":1200,"height":628,"url":"https:\/\/www.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/1220-x-628-OGIMAGE_Run-Docker-and-Portainer-on-a-VPS-2026-Guide_1x.webp","type":"image\/webp"}],"author":"Dallas Kashuba","twitter_card":"summary_large_image","twitter_title":"How To Run Docker on a VPS With Portainer (2026)","twitter_description":"Install Docker and Portainer CE on an Ubuntu VPS in 7 steps \u2014 firewall first, then the one-command install, sizing math, backups, and real costs.","twitter_creator":"@http:\/\/twitter.com\/dallas","twitter_site":"@dreamhost","twitter_misc":{"Written by":"Dallas Kashuba","Est. reading time":"15 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/#article","isPartOf":{"@id":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/"},"author":{"name":"Dallas Kashuba","@id":"https:\/\/www-dev.dreamhost.com\/blog\/#\/schema\/person\/f6a12ad459538e8e418cf31b8b603814"},"headline":"How To Run Docker on a VPS With Portainer (2026 Guide)","datePublished":"2026-06-19T14:30:00+00:00","dateModified":"2026-08-26T03:55:49+00:00","mainEntityOfPage":{"@id":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/"},"wordCount":4944,"publisher":{"@id":"https:\/\/www-dev.dreamhost.com\/blog\/#organization"},"image":{"@id":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/#primaryimage"},"thumbnailUrl":"https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/1460x1095-BLOG-HERO-Run-Docker-and-Portainer-on-a-VPS-2026-Guide_1x.webp","articleSection":["Tutorials","VPS Hosting","Web Hosting"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/","url":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/","name":"How To Run Docker on a VPS With Portainer (2026)","isPartOf":{"@id":"https:\/\/www-dev.dreamhost.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/#primaryimage"},"image":{"@id":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/#primaryimage"},"thumbnailUrl":"https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/1460x1095-BLOG-HERO-Run-Docker-and-Portainer-on-a-VPS-2026-Guide_1x.webp","datePublished":"2026-06-19T14:30:00+00:00","dateModified":"2026-08-26T03:55:49+00:00","description":"Install Docker and Portainer CE on an Ubuntu VPS in 7 steps \u2014 firewall first, then the one-command install, sizing math, backups, and real costs.","breadcrumb":{"@id":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/#primaryimage","url":"https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/1460x1095-BLOG-HERO-Run-Docker-and-Portainer-on-a-VPS-2026-Guide_1x.webp","contentUrl":"https:\/\/www-dev.dreamhost.com\/blog\/wp-content\/uploads\/2026\/06\/1460x1095-BLOG-HERO-Run-Docker-and-Portainer-on-a-VPS-2026-Guide_1x.webp","width":1460,"height":1095,"caption":"How To Run Docker and Portainer on a VPS (2026 Guide)"},{"@type":"BreadcrumbList","@id":"https:\/\/www-dev.dreamhost.com\/blog\/docker-and-portainer-vps\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.dreamhost.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How To Run Docker on a VPS With Portainer (2026 Guide)"}]},{"@type":"WebSite","@id":"https:\/\/www-dev.dreamhost.com\/blog\/#website","url":"https:\/\/www-dev.dreamhost.com\/blog\/","name":"DreamHost Blog","description":"","publisher":{"@id":"https:\/\/www-dev.dreamhost.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www-dev.dreamhost.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www-dev.dreamhost.com\/blog\/#organization","name":"DreamHost","url":"https:\/\/www-dev.dreamhost.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www-dev.dreamhost.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/dhblog.dream.press\/blog\/wp-content\/uploads\/2019\/01\/dh_logo-blue-2.png","contentUrl":"https:\/\/dhblog.dream.press\/blog\/wp-content\/uploads\/2019\/01\/dh_logo-blue-2.png","width":1200,"height":168,"caption":"DreamHost"},"image":{"@id":"https:\/\/www-dev.dreamhost.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/DreamHost\/","https:\/\/x.com\/dreamhost","https:\/\/www.instagram.com\/dreamhost\/","https:\/\/www.linkedin.com\/company\/dreamhost\/","https:\/\/www.youtube.com\/user\/dreamhostusa"]},{"@type":"Person","@id":"https:\/\/www-dev.dreamhost.com\/blog\/#\/schema\/person\/f6a12ad459538e8e418cf31b8b603814","name":"Dallas Kashuba","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/05ab6fb1606be97d16c74efb0fba01b3c11e91501dc5f9661bdfbb5c5b0cd113?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/05ab6fb1606be97d16c74efb0fba01b3c11e91501dc5f9661bdfbb5c5b0cd113?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/05ab6fb1606be97d16c74efb0fba01b3c11e91501dc5f9661bdfbb5c5b0cd113?s=96&d=mm&r=g","caption":"Dallas Kashuba"},"description":"Dallas Kashuba co-founded DreamHost while attending Harvey Mudd College and has spent nearly three decades building infrastructure at scale. Today he serves as an advisor, board member, and investor for various tech startups, with a consistent focus on user privacy, open source, and data portability. When he's not thinking about the Open Web, he's probably making music. Follow Dallas on X.","sameAs":["http:\/\/www.idallas.com\/","https:\/\/x.com\/http:\/\/twitter.com\/dallas"],"url":"https:\/\/www-dev.dreamhost.com\/blog\/author\/dallas\/"}]}},"lang":"en","translations":{"en":82006,"es":82076,"uk":82095,"ru":82089,"pt":82068,"pl":82086,"de":82070,"nl":82080,"fr":82074,"it":82082},"pll_sync_post":{},"_links":{"self":[{"href":"https:\/\/www-dev.dreamhost.com\/blog\/wp-json\/wp\/v2\/posts\/82006","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www-dev.dreamhost.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www-dev.dreamhost.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www-dev.dreamhost.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"version-history":[{"count":5,"href":"https:\/\/www-dev.dreamhost.com\/blog\/wp-json\/wp\/v2\/posts\/82006\/revisions"}],"predecessor-version":[{"id":90232,"href":"https:\/\/www-dev.dreamhost.com\/blog\/wp-json\/wp\/v2\/posts\/82006\/revisions\/90232"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www-dev.dreamhost.com\/blog\/wp-json\/wp\/v2\/media\/82008"}],"wp:attachment":[{"href":"https:\/\/www-dev.dreamhost.com\/blog\/wp-json\/wp\/v2\/media?parent=82006"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www-dev.dreamhost.com\/blog\/wp-json\/wp\/v2\/categories?post=82006"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www-dev.dreamhost.com\/blog\/wp-json\/wp\/v2\/tags?post=82006"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}