Docker Basics for Self-Hosting: A Beginner’s Guide

Developer viewing Docker container logs on a home server terminal

Docker is the reason self-hosting got easy: it packages an app and every dependency it needs into one image, so installing a complex service becomes a single config file and one command instead of an afternoon of dependency hell. On my own stack every service runs as a container, and a single docker compose up -d brings a dozen of them online in under a minute.

If you are coming to self-hosting from scratch, Docker is the layer to learn first — everything in the self-hosting guide sits on top of it. This is the explanation I give people when they ask why their Nextcloud install keeps breaking: they are installing software the old way when the whole ecosystem moved to containers years ago. Get four concepts straight and the rest falls into place.

What Docker Actually Does

Docker runs applications in containers — isolated, lightweight packages that include the app plus the exact libraries and runtime it needs. Unlike a virtual machine, a container shares the host’s kernel, so it starts in milliseconds and uses a fraction of the memory. A mini PC that could run two or three VMs will happily run twenty containers.

The problem it solves is dependency conflict. One app wants PHP 8.1, another wants 8.3, a third needs a specific database version — install them all on one OS and they fight. Containers end that: each app brings its own dependencies inside its image, completely isolated from the others. That is why “deploy this app” went from a wiki page of steps to a copy-paste of one file. It is also why almost every self-hosted project today ships a Docker image as the official, recommended way to run it.

The Four Concepts That Unlock Everything

Ninety percent of Docker documentation makes sense once you understand four words: image, container, volume, and network. An image is the read-only template; a container is a running instance of that image; a volume is persistent storage that survives the container being rebuilt; and a network is how containers talk to each other and the outside world.

Here is the mental model that made it click for me. The image is the recipe. The container is the meal you cooked from it — you can throw it away and cook another from the same recipe any time. The volume is the pantry, where the ingredients you want to keep live, untouched when you rebuild the meal. The network is the dining room, where the dishes are passed between each other. The single most important consequence: data inside a container is disposable; data in a volume persists. Store your database in a named volume, never inside the container, or a routine update wipes it.

A terminal window showing a docker compose file and containers starting up on a homelab mini PC

Docker Compose: The Format You Will Actually Use

Nobody self-hosts by typing long docker run commands. The real workflow is Docker Compose — a single YAML file that describes your whole stack: which images to run, what volumes to mount, which ports to expose, and how containers connect. One file, one command, and the service is up; edit the file and re-run to change anything.

The reason Compose matters so much is that the file is your documentation. Six months later, when you have forgotten exactly how you configured a service, the docker-compose.yml tells you precisely — every setting in one place, version-controllable, and trivially reproducible on another machine. When a project’s docs say “here is our Compose file,” you can read it top to bottom and know exactly what it does before you run it. I keep every service’s Compose file in a folder structure on the host so the entire stack is rebuildable from scratch in minutes.

A Real Example: Your First Container

The cleanest first service is a network-wide ad blocker — useful immediately and a perfect Compose learning exercise. The structure is always the same: a service name, the image to pull, a restart policy, the ports to map, the volumes for persistent config, and any environment variables. Once you have written one, you have written them all.

Three habits to build from day one. First, always set restart: unless-stopped so containers come back after a reboot — the most common “why did my service disappear” is a missing restart policy. Second, always map config and data to named volumes, never leave them inside the container. Third, pin image versions rather than blindly running latest on critical services, so an upstream change does not silently break your stack overnight. None of this is advanced; it is just the discipline that separates a stack that runs for a year from one you are constantly rescuing.

A self-hosted dashboard showing multiple Docker containers running healthy on a home server

Where to Run Docker

Docker runs on almost anything: a Raspberry Pi, an old laptop, an N100 mini PC, or a VM on a hypervisor. For a first stack I would put it on a dedicated mini PC with 16GB of RAM — quiet, low-power, and powerful enough for a dozen services. When you outgrow a single box, you graduate to running Docker inside VMs or containers on Proxmox, which is the path most homelabs take.

One decision worth making early: where the persistent data lives. Containers are disposable, but your volumes are not, so the host’s storage needs to be something you back up. In my setup the Docker host keeps its config locally but its data volumes are backed up to the NAS on a schedule, and from there off-site. That single arrangement means I can lose the entire Docker host, rebuild it from the Compose files, restore the volumes, and be back exactly where I was. Treat the host as replaceable and the data as precious — the same instinct that runs through every layer of a good homelab.

Networking and Security for Containers

Containers get their own internal network by default, and you only expose the ports you explicitly map. That is already safer than installing services directly on the host, but it is not a security boundary on its own. Anything you expose beyond the host belongs behind a reverse proxy and, ideally, on a segmented VLAN.

The pattern I run: containers talk to each other over an internal Docker network using service names, the reverse proxy is the only thing with a published port, and the whole Docker host sits on a segment that cannot reach my trusted LAN if something gets compromised. This is where Docker and home networking meet — the same segmentation discipline that cages IoT devices applies to anything running code you did not write. A container is convenient isolation, not a firewall; put the firewall around it.

Keeping Containers Updated

Updating a container is just pulling a newer image and recreating the container from it — your volumes stay put, so the data carries over untouched. With Compose it is two commands: pull the new images, then bring the stack back up, and the old containers are replaced in seconds. Because the data lives in volumes, a bad update is a quick roll back to the previous image tag.

The temptation is to automate updates with a tool like Watchtower so everything tracks latest automatically. I do not, on anything I care about. An unattended update that pulls a breaking change at 3am is exactly the kind of silent failure that turns a hobby into a chore. My rule: pin versions on the services that matter, update them deliberately when I have ten minutes to watch the logs, and take a snapshot of the host first so rolling back is trivial. Convenience automation is fine for throwaway containers; for the stack the household depends on, deliberate beats automatic.

Common Beginner Mistakes With Docker

The mistakes that send beginners back to square one are nearly always the same three. Storing data inside the container instead of a named volume, so a rebuild wipes everything. Running every service as root with no thought to permissions. And exposing container ports directly to the internet instead of putting them behind a proxy and a VPN.

A couple more worth flagging early: forgetting the restart policy, so services vanish after a reboot; and mounting half a dozen services at the same host path so their configs collide. None of these are hard to avoid — they are just the lessons everyone learns the painful way if nobody tells them first. Map data to volumes, set a restart policy, mind your ports, and keep each service’s config in its own folder, and your first stack will outlast your enthusiasm for tinkering with it.

Frequently Asked Questions

Is Docker hard to learn for a complete beginner?

No. The core is four concepts: image, container, volume, and network. Most self-hosting uses Docker Compose, where you edit a single YAML file and run one command. You can host real services in an afternoon without knowing any programming.

What is the difference between Docker and a virtual machine?

A VM virtualizes an entire operating system and uses gigabytes of RAM. A Docker container shares the host kernel and isolates just the app, starting in milliseconds and using far less memory. A mini PC that runs two or three VMs can run twenty containers.

Will I lose my data when I update a Docker container?

Only if you stored data inside the container instead of a named volume. Volumes persist when a container is rebuilt or updated. Always map databases and config to named volumes, and a routine update never touches your data.

Do I need to know Linux command line to use Docker?

A little helps, but not much. You mostly edit a YAML file and run two or three commands like docker compose up and docker compose down. You will pick up basic Linux naturally, but you do not need to be fluent to start.

Should I use docker run or Docker Compose?

Use Compose for anything you intend to keep. A Compose file documents your entire configuration in one place, is reproducible on another machine, and brings a whole stack up with one command. Reserve docker run for quick throwaway tests.

Related Guides

Leave a Comment

Your email address will not be published. Required fields are marked *