Most self-hosted software ships as a Docker image now. If you want to run something on a VPS, the installation instructions almost certainly start with docker compose up. The reason is straightforward: each service gets its own container with its own filesystem, libraries, and configuration. Nothing conflicts with anything else on the host. Redeploying means pulling a new image. Rolling back means pointing to an older tag.

If you have already set up a VPS for the first time and completed the security checklist, adding Docker is a natural next step. This guide covers installation, Docker Compose basics, resource planning, and the practical trade-offs that matter on a VPS specifically.

Before You Install: the KVM Requirement

Docker relies on kernel features (namespaces, cgroups, overlay filesystems) that require full kernel access. On a KVM-based VPS, these work out of the box because your server runs its own kernel. On an OpenVZ VPS, Docker either does not work at all or requires the provider to have enabled specific kernel configurations that you cannot control.

If you are not sure which virtualization type your plan uses, check your provider's plan page or dashboard. The KVM vs OpenVZ comparison covers this in detail, including why Docker compatibility is one of the key reasons KVM has become the standard for most serious workloads. If your current plan runs OpenVZ and you need Docker, switching to a KVM plan is the straightforward fix.

The other prerequisite: an unmanaged VPS with root (or sudo) access. Managed plans that restrict root access may not let you install Docker, or the provider may have their own container tooling instead.

Installing Docker on Ubuntu or Debian

The official Docker documentation recommends installing from Docker's own apt repository rather than your distribution's default packages. Distribution-packaged Docker tends to lag behind significantly, and certain features (especially Compose V2) may not be available.

The process in summary:

  1. Update your package index and install prerequisites for adding HTTPS repositories
  2. Add Docker's official GPG key
  3. Add the Docker apt repository for your distribution
  4. Install docker-ce, docker-ce-cli, containerd.io, and docker-compose-plugin

After installation, verify it works:

sudo docker run hello-world

This pulls a tiny test image and runs it. If you see a "Hello from Docker!" message, the engine is running correctly.

Adding your user to the docker group lets you run Docker commands without sudo:

sudo usermod -aG docker $USER

Log out and back in for the group membership to take effect. Be aware of what this means: anyone in the docker group can interact with the Docker daemon, which has root-level access to the host. On a VPS you manage alone, this is convenient. On a shared server, consider whether that level of access is appropriate for each user.

Installing Docker on RHEL, Rocky Linux, or AlmaLinux

The process is similar but uses dnf instead of apt:

  1. Install the yum-utils package
  2. Add Docker's repository with dnf config-manager
  3. Install docker-ce, docker-ce-cli, containerd.io, and docker-compose-plugin
  4. Start and enable the Docker service with systemctl

The OS selection guide covers the broader differences between distribution families if you are still deciding which one to run.

Docker Compose: the Practical Way to Manage Services

Running individual docker run commands works for a single container. Once you are deploying two or more services (a web application and its database, for example), Docker Compose is what keeps the configuration manageable.

A compose.yaml file defines every service, its image, port mappings, volumes, environment variables, and how services connect to each other. A common starting point on a VPS looks something like this:

services:
  app:
    image: your-application:latest
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=postgres://db_user:db_pass@db:5432/appdb
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=db_user
      - POSTGRES_PASSWORD=db_pass
      - POSTGRES_DB=appdb
    restart: unless-stopped

volumes:
  pgdata:

Start everything with:

docker compose up -d

The -d flag runs containers in the background. Stop them with docker compose down. Update an image and redeploy with docker compose pull followed by docker compose up -d again.

Three Compose patterns worth getting right on a VPS from the start:

  • restart: unless-stopped ensures containers come back after a reboot or a crash. Without this, a power cycle on the host means manually restarting everything.
  • Named volumes (like pgdata above) persist data outside the container. If you remove and recreate a container, the volume survives. Losing the volume means losing the data. Back it up.
  • Sensitive values in environment variables should not live in the compose.yaml file itself if that file is committed to a repository. Use a .env file next to the Compose file, or Docker secrets for more structured setups.

How Much VPS Do You Need for Docker?

Docker itself imposes minimal overhead. The daemon typically uses around 50 to 100 MB of RAM at idle. The resource cost comes from the containers you run, not from Docker itself.

That said, containerized deployments tend to use slightly more memory than the same software installed directly on the host. Each container carries its own libraries and runtime, and the base images for popular stacks (Node.js, Python, Java) are not tiny. An application that uses 200 MB installed natively might use 250 to 350 MB in a container, depending on the base image.

Rough guidance for planning:

Workload Minimum RAM Comfortable RAM
Single lightweight app (static site, proxy) 512 MB 1 GB
Web app with a database 1 GB 2 GB
Three to four services (app, DB, cache, reverse proxy) 2 GB 4 GB
Full self-hosted stack (multiple tools) 4 GB 8 GB

Storage fills up faster than people expect. Docker images, layers, build caches, and container logs accumulate over time. Run docker system prune periodically to reclaim space from stopped containers, unused images, and dangling build cache. On a VPS with limited disk, scheduling this as a cron job is a reasonable precaution.

For choosing a provider with the right specs, the providers directory has user-reviewed ratings on performance, reliability, and value for money.

Putting a Reverse Proxy in Front

Most VPS setups that run multiple Docker containers also run a reverse proxy to route incoming traffic. Instead of exposing each service on its own port, the proxy listens on ports 80 and 443 and routes requests to the correct container based on the hostname or path.

Nginx, Caddy, and Traefik are the three common choices. Each works well with Docker:

  • Nginx is the most widely documented. You write a configuration file per site and manage SSL certificates separately (usually with Certbot or a similar ACME client).
  • Caddy handles SSL automatically through built-in ACME support. Point a domain at your server, configure Caddy, and it obtains and renews the certificate with no extra setup.
  • Traefik integrates directly with Docker labels, making it possible to add new services to the proxy by adding labels to the Compose file rather than editing a separate proxy config.

All three can run as Docker containers themselves. The proxy container maps ports 80 and 443 on the host, and other containers communicate with it over a shared Docker network without exposing their ports publicly.

Which one to use depends on how many services you plan to run and how much configuration you want to manage. For a single application, Caddy requires the least setup. For a VPS hosting several services with frequent additions, Traefik's label-based approach reduces the per-service configuration burden.

Docker Security on a VPS

Docker containers share the host kernel. A vulnerability in the kernel, or a misconfigured container running as root, can potentially affect the host and every other container on it. This matters more on a VPS than on a local development machine because the server is reachable from the public internet.

Practical steps that reduce the risk:

  1. Run containers as a non-root user. Most official Docker images support this. Set user: "1000:1000" in your Compose file or pick images that drop privileges by default. Only run as root when the application explicitly requires it.
  2. Pull fresh base images regularly. Stale images accumulate CVEs quietly. Rebuild after pulling to pick up dependency patches.
  3. Drop unnecessary Linux capabilities. Docker grants containers a default set they rarely need. Use cap_drop: [ALL] in Compose and cap_add only the specific capabilities the application requires.
  4. Bind internal services to 127.0.0.1, not 0.0.0.0. A database container should talk to other containers through the Docker network, not listen on a public interface.
  5. Docker manipulates iptables directly, which catches people off guard. A container port published with -p 8080:8080 is reachable from the internet even if UFW does not have port 8080 open. The fix: bind to localhost on the host side (127.0.0.1:8080:8080) or configure Docker to respect your host firewall.

The VPS security checklist covers host-level hardening. Docker adds a layer on top of that which needs its own attention.

Docker vs. Direct Installation

Docker solves real problems on a VPS. Dependency isolation is the big one: running multiple applications that need different versions of the same library or runtime is painful without containers and trivial with them. Reproducible deployments are another benefit. The same image that works in testing works in production, because the image carries everything it needs.

That said, Docker is not always the right tool. For a single application on a dedicated VPS (a WordPress site, one web app, a standalone database), installing directly on the host is simpler. Fewer moving parts, more straightforward debugging, and marginally better performance without the container layer. Docker also requires understanding image management, volume persistence, container networking, and the firewall interaction described above. If the application has a clean installation process and no conflicting dependencies, the container layer may add overhead without a matching benefit.

Scenario Better approach Why
Multiple services sharing one VPS Docker Dependency isolation, clean separation
Applications that ship as Docker images Docker Deployment and rollback are trivial
Single-purpose server, one application Direct install Less complexity, easier debugging
RAM-constrained plan, every MB counts Direct install No per-container memory overhead
Team unfamiliar with containers Direct install Learning curve is real; do not force it

Getting Started

If you have a KVM VPS with root access and a Linux distribution installed, Docker installation is quick. Pick a service you want to run, write a Compose file, bring it up, and iterate from there. The self-hosted tools article covers ten applications worth starting with if you need ideas.

For choosing a VPS provider with the specs and virtualization type that Docker requires, browse the reviewed providers and check what real users say about performance and reliability. And if you are still early in the VPS journey, the first-time setup guide covers everything before Docker: SSH, security, and getting a working server from a blank slate.