serverserv serverserv

How to install Docker and Docker Compose on a VPS and run your first app

By the serverserv team · 6 min read · Updated

To install Docker on a VPS running Ubuntu or Debian, add Docker's official apt repository, install the docker-ce and docker-compose-plugin packages, and test with the hello-world image. It takes about ten minutes on a KVM server. This guide gives the exact commands, runs a first app with Docker Compose, and covers the firewall trap and the upkeep that many tutorials skip.

What do you need before you install Docker on a VPS?

You need a KVM VPS with a 64-bit Ubuntu or Debian release, a sudo user, and enough RAM and disk for your containers. KVM matters because Docker relies on Linux kernel features, and container-based VPS types such as OpenVZ share the host's kernel and often cannot run Docker properly.

Docker's documentation lists the supported releases. At the time of writing these include Ubuntu 22.04 and 24.04 LTS and Debian 12 and 13. Check yours with cat /etc/os-release and uname -m, which should print x86_64 or aarch64. If the server is brand new, work through how to secure a Linux VPS first.

What you will runRAMDiskserverserv plan
Learning Docker, one to three small containers2 GB25 GBKVM Mini
A web app with its database and a reverse proxy4 GB50 GBKVM 1
A Compose stack of 5 to 10 services, or n8n with PostgreSQL8 GB100 GBKVM 2
Several stacks, image builds on the server, CI runners16 GB200 GBKVM 3

One 2026 buying guide uses a rule of thumb of about 1 GB of RAM for every two or three light containers, at least 40 GB of disk, and 80 GB or more if you build images on the server. Images, build cache and logs grow faster than most people expect. KVM Mini's 25 GB is fine for learning, but start at KVM 1 or above for anything you plan to keep.

Step 1: add Docker's official apt repository

Install Docker from Docker's own repository rather than the docker.io package in Ubuntu or Debian, so you get current releases and the Compose plugin. First remove any older packages that could conflict.

  1. Remove conflicting packages (it is fine if apt says none are installed): for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove -y $pkg; done
  2. Install the tools the setup needs: sudo apt-get update && sudo apt-get install -y ca-certificates curl
  3. Create the keyring folder: sudo install -m 0755 -d /etc/apt/keyrings
  4. Download Docker's signing key: sudo curl -fsSL https://download.docker.com/linux/$(. /etc/os-release && echo "$ID")/gpg -o /etc/apt/keyrings/docker.asc
  5. Make it readable: sudo chmod a+r /etc/apt/keyrings/docker.asc
  6. Add the repository: echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$(. /etc/os-release && echo "$ID") $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

These lines read your distribution name and release codename from /etc/os-release, so the same commands work on Ubuntu and Debian. Docker's documentation shows separate versions for each distribution if you prefer to copy from there.

You may also see the one-line script at get.docker.com. Docker describes it as a convenience for testing and development, so use the repository method above on a server you care about.

Step 2: install Docker Engine and Docker Compose

Install five packages, make sure Docker starts on boot, and run the hello-world image. Compose now ships as a plugin, so the command is docker compose with a space.

  1. sudo apt-get update
  2. sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  3. sudo systemctl enable --now docker
  4. sudo docker run hello-world downloads a tiny test image and prints a short welcome message if everything works.
  5. docker compose version confirms the Compose plugin is installed.
PackageWhat it does
docker-ceThe Docker Engine service that runs containers
docker-ce-cliThe docker command you type
containerd.ioThe container runtime Docker uses underneath
docker-buildx-pluginBuilds images with docker build
docker-compose-pluginRuns multi-container apps with docker compose

Older tutorials use docker-compose with a hyphen. That is Compose version 1, which Docker has retired. The plugin reads the same compose files.

To run Docker without typing sudo, add your user to the docker group with sudo usermod -aG docker $USER, then log out and back in. Be aware that membership of the docker group is equal to root access on that server, so add only accounts you trust.

Step 3: run your first app with Docker Compose

Create a project folder, write a compose.yaml file, and start it with docker compose up -d. The example below runs nginx serving one page, bound to localhost so it is not public until you decide it should be.

  1. mkdir -p ~/hello/html && cd ~/hello
  2. echo '<h1>Hello from my VPS</h1>' > html/index.html
  3. echo '{services: {web: {image: "nginx:stable", restart: unless-stopped, ports: ["127.0.0.1:8080:80"], volumes: ["./html:/usr/share/nginx/html:ro"]}}}' > compose.yaml
  4. docker compose up -d
  5. docker compose ps shows the container running.
  6. curl http://127.0.0.1:8080 returns your page.

The compose file above is valid YAML written on one line. In an editor such as nano you would usually spread it over several lines, but it says the same thing: one service called web, using the nginx:stable image, restarted unless you stop it, with port 8080 on localhost mapped to port 80 in the container and your html folder mounted read-only.

To see the page from your own computer, open an SSH tunnel with ssh -L 8080:127.0.0.1:8080 [email protected] and browse to localhost:8080. Other everyday commands are docker compose logs -f to follow logs, docker compose down to stop, and docker compose pull && docker compose up -d to update.

Real apps follow the same pattern. n8n's own documentation recommends Docker Compose for self-hosting, and notes that installing with npm is deprecated from n8n 3.0. See our guide to self-hosting n8n for what else that needs.

Why do Docker ports bypass ufw, and how do you fix it?

When you publish a port such as 8080:80, Docker adds its own firewall rules that are checked before ufw's rules, so the port is reachable from the internet even if ufw shows it as closed. The fix is to bind ports to 127.0.0.1 and expose only a reverse proxy.

  • Publish to localhost: write 127.0.0.1:8080:80 instead of 8080:80 for anything that is not meant to be public.
  • Let one reverse proxy face the internet: run Caddy, nginx or Traefik on ports 80 and 443 with HTTPS, and send traffic to your apps on localhost. Allow those ports in ufw too, so your rules match reality.
  • Give databases no ports at all: containers in the same Compose project reach each other by service name, for example host db on port 5432.
  • Test from outside: from your own computer, nc -zv 203.0.113.10 8080 should fail or time out.

Check each image's defaults, too. OpenClaw's security documentation is a good example: its gateway binds to loopback on a normal install, but its container images default to an exposed bind and need authentication set up.

How do you keep Docker updated and stop the disk filling up?

Docker Engine updates arrive through apt, while images only change when you pull them. Old images and unrotated logs are the usual reason a Docker VPS runs out of disk.

  • Engine: run sudo apt-get update && sudo apt-get upgrade regularly. Automatic security updates on Ubuntu and Debian cover the distribution's own packages by default, not Docker's repository.
  • Images: in each project folder, run docker compose pull && docker compose up -d, then docker image prune -f to remove the old versions.
  • Disk: docker system df shows what images, containers, volumes and build cache use.
  • Logs: the default json-file log driver never rotates. Switch to the local driver, which does: echo '{"log-driver": "local"}' | sudo tee /etc/docker/daemon.json, then sudo systemctl restart docker. It applies to new containers, so recreate yours with docker compose up -d --force-recreate.
  • Backups: your data lives in named volumes under /var/lib/docker/volumes and in bind-mounted folders. Back those up along with compose.yaml and any .env files, and dump databases before you copy them.

Which serverserv VPS should you run Docker on?

Every serverserv Linux KVM VPS runs Docker, because each one is a full KVM virtual machine with root access. KVM 2 suits most Compose stacks.

  • KVM Mini (1 vCPU, 2 GB RAM, 25 GB NVMe): $8.99 monthly, or $6.29 a month on a 3-year term. For learning and a few small containers.
  • KVM 1 (1 vCPU, 4 GB RAM, 50 GB NVMe): $14.99 monthly, or $10.49 a month on 3 years.
  • KVM 2 (2 vCPU, 8 GB RAM, 100 GB NVMe): $23.99 monthly, or $16.49 a month on 3 years.
  • KVM 3 (4 vCPU, 16 GB RAM, 200 GB NVMe): $39.99 monthly, or $27.99 a month on 3 years.

Choose your operating system in the order form; the list shown there is what is available. Every term renews at the price you paid. If you only want one app, n8n hosting and OpenClaw hosting come with the software set up for you, and Managed VPS suits teams who would rather not run the server. Not sure which fits? open a ticket and tell us what you plan to run.

Questions

Can I run Docker on any VPS?

Only on VPS types that give you your own kernel, such as KVM. OpenVZ and LXC containers share the host's kernel and often cannot run Docker without special host settings. Every serverserv Linux VPS uses KVM.

How much RAM do I need for a Docker VPS?

Docker itself uses little memory; your containers decide. Start with 2 GB for learning, 4 GB for an app with a database, and 8 GB for a Compose stack of several services.

Why does my Docker VPS keep running out of disk space?

Usually old images, build cache and container logs that never rotate. Run docker system df, prune unused images and switch the log driver to local.

Do I need a managed VPS for Docker?

No, if you are comfortable on the Linux command line, an unmanaged KVM VPS is enough. Choose Managed VPS if you want our team to look after the server.

Is docker-compose the same as docker compose?

docker-compose with a hyphen is the retired version 1 tool. docker compose with a space is the current plugin, and it reads the same compose files.

Sources

Prices from other companies are as published at the time of writing and can change.

Ready to deploy?

Pick a plan, choose a term and you are online in minutes.

See all plansTalk to sales

Need a custom server, bulk pricing or help moving from another host? Our team will build a quote for you.

Plans from $3.99/moSee plans