Top Docker Mistakes Beginners Make in Their Homelab and How to Fix Them
Avoid the most common Docker pitfalls that trip up homelab beginners, from sudo abuse and port conflicts to missing restart policies and volume leaks.
Avoid the most common Docker pitfalls that trip up homelab beginners, from sudo abuse and port conflicts to missing restart policies and volume leaks.
Docker is the most popular container runtime in the self-hosting ecosystem, and for good reason. Its massive documentation, straightforward Compose facility, and compatibility with thousands of first- and third-party images make it the go-to choice for anyone running a homelab. But with great power comes great responsibility. Beginners often make a handful of predictable mistakes that lead to security vulnerabilities, data loss, and hours of head-scratching debugging.
This article covers the most common Docker mistakes beginners make in their first month of self-hosting—and how to avoid each one. Whether you are running Docker on a Raspberry Pi, an old desktop running Linux, or a Proxmox VM, these tips will keep your containers stable and your host secure.
When you first install Docker, you quickly discover that running docker ps without elevated privileges returns a permission error. The quick fix many beginners reach for is prefixing every Docker command with sudo.
Why this is a problem: Running Docker with sudo makes the host more vulnerable to privilege escalation and container escape problems. The Docker daemon runs as root, and any user in the docker group effectively has root-level access to the host. Adding sudo on top introduces unnecessary complexity and can mask permission issues that should be addressed properly.
How to fix it: Instead of using sudo, add your user to the docker group:
sudo usermod -aG docker $USERAfter running this command, log out and log back in (or restart your session) for the group change to take effect. Once that is done, you can run Docker commands without sudo.
Note: Granting docker group membership still gives the user significant power—equivalent to root access over the Docker daemon. Only add trusted users to this group.Port mapping is one of the first things you learn in Docker. The -p 80:80 flag maps a container port to a host port. Beginners often expose ports to 0.0.0.0 (the default), which makes the service accessible from any network interface on the host.
Why this is a problem: If your homelab server is connected to the internet or even just your local LAN, exposing a port without consideration means anyone on that network can reach the service. If the service has no authentication, or if it has a known vulnerability, you are opening the door to unwanted access.
How to fix it:-p 127.0.0.1:8080:80docker ps to verify which ports are currently in use and avoid conflicts with already-running containers.# Only listen on localhost
docker run -p 127.0.0.1:8080:80 my-applatest Tag for EverythingThe convenience of docker pull my-image:latest is hard to resist. But relying on the latest tag is one of the most common Docker mistakes beginners make.
Why this is a problem: The latest tag is a moving target. Pulling latest today might give you a different image than pulling it tomorrow. This breaks reproducibility—you cannot guarantee that your development, staging, and production environments are running the same image. A breaking change in a new upstream release can take down your homelab service unexpectedly.
How to fix it: Pin your images to specific version tags or digests.
# In docker-compose.yml
services:
my-app:
image: my-app:2.1.0 # Use a specific version, not 'latest'If you are building your own images, tag them with version numbers and push them to a registry. For third-party images, check the image's Docker Hub page or repository documentation for supported version tags and pin to a known stable release.
By default, when you run a Docker container, it does not restart automatically. If the container crashes or the host reboots, the container stays stopped until you manually start it again.
Why this is a problem: In a homelab, you want your services to be resilient. A power outage, an OOM (out-of-memory) kill, or a simple crash should not leave your services offline indefinitely.
How to fix it: Set a restart policy on every container. Docker Compose makes this easy:
services:
my-app:
image: my-app:2.1.0
restart: unless-stoppedThe restart policy options are:
no — Do not automatically restart (default).always — Always restart regardless of the exit code.on-failure — Restart only when the container exits with a non-zero exit code.unless-stopped — Restart unless the container was explicitly stopped by docker stop. This is the recommended policy for most homelab services.Note: Withunless-stopped, a container will not restart after you deliberately stop it withdocker stop, which is usually the behavior you want during maintenance.
Over time, a Docker host accumulates unused images, stopped containers, and dangling volumes. Beginners often ignore this until disk space runs out.
docker system dfRunning this command shows you how much disk space images, containers, volumes, and build cache are consuming.
Why this is a problem: Stale images and orphaned volumes can eat up gigabytes of storage. On a small SSD in a homelab server, this quickly becomes a problem. Stopped containers also clutter the output of docker ps -a and make it harder to see what is actually running.
How to fix it: Run periodic cleanup commands:
# Remove all stopped containers
docker container prune
# Remove all unused images
docker image prune
# Remove all unused volumes (be careful, this deletes persistent data)
docker volume prune
# Do it all at once
docker system prune -aAdd a cron job or schedule a periodic cleanup via a script. Many homelab users run docker system prune -a --volumes once a week to keep things lean.
Warning: docker volume prune removes volumes not referenced by any container. If you stop a container but intend to reuse its data later, make sure the volume is still referenced, or back it up first.When you are done tinkering with a service and run docker rm container_name, the container is removed—but its persistent volumes remain on the Docker host.
Why this is a problem: Orphaned volumes accumulate and take up disk space. They also contain potentially sensitive data. If you deployed a database container, removed it, and later redeployed, you might end up with stale or conflicting data.
How to fix it: Use the -v flag when removing a container to delete its associated volumes:
docker rm -v container_nameOr, if you are using Docker Compose, clean up everything with:
docker compose down -vBe extremely careful with the -v flag—this permanently deletes the volume data. Always ensure you have a backup of important data before removing volumes.
By default, most Docker images run their processes as the root user inside the container. Beginners often do not realize this is happening.
Why this is a problem: Running containers as root creates a security risk. If an attacker compromises the container, they may be able to escalate privileges to the Docker daemon or the host system. Even without malicious intent, a misconfigured container running as root can write files to host-mounted volumes with root ownership, causing permission headaches.
How to fix it:PUID and PGID environment variable or a USER directive in their Dockerfile.RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuserservices:
my-app:
image: my-app:2.1.0
user: "1000:1000"A common pattern in self-hosting tutorials is mounting /var/run/docker.sock into a container so that the container can manage other containers. Tools like Portainer, Watchtower, and some backup scripts rely on this.
Why this is a problem: Mounting /var/run/docker.sock gives the container direct API access to the Docker daemon. This means the container can start new containers, mount host directories, modify running containers, and more. If that container is compromised, the attacker gains near-complete control over your Docker host.
When you run a container without specifying a network, it joins the default bridge network. Beginners often deploy several services without giving any thought to how they communicate.
Why this is a problem: On the default bridge network, containers can only communicate by IP address, not by service name. This makes service discovery brittle—if a container restarts and gets a new IP, other containers trying to reach it will break.
How to fix it: Use user-defined networks with Docker Compose. Compose automatically creates a network for your project, and containers can reach each other using the service name as the hostname.
services:
web:
image: nginx:alpine
depends_on:
- api
api:
image: my-api:2.1.0In this setup, the web container can reach the api container by hostname api. No need to worry about IP addresses.
.dockerignore FileWhen building a Docker image, the Docker client sends the entire build context (the directory you specify) to the Docker daemon. Beginners often include unnecessary files like node_modules, .git directories, and local configuration files.
Why this is a problem: A bloated build context slows down builds, increases image size, and can accidentally include sensitive files (like .env with API keys) inside the image.
How to fix it: Create a .dockerignore file in your build context directory:
.git
node_modules
.env
*.md
.gitignoreThis file works exactly like .gitignore—it excludes matching files and directories from the build context, resulting in faster builds and smaller images.
Docker is an incredibly powerful tool for self-hosting, but beginners often stumble on the same set of issues: security misconfigurations, lack of restart policies, port mismanagement, and neglecting cleanup. By avoiding these ten common mistakes, you will run a safer, more reliable homelab.
Here is a quick checklist to keep handy:
docker group instead of using sudo.latest.restart: unless-stopped on every service..dockerignore file in your build context.With these practices in place, you can focus on what really matters: building a homelab you love.