Almost everything on my home server, including this blog, my photos (Immich) and my password manager, runs in Docker. Docker is the foundation, and Portainer is the dashboard I use to keep an eye on it all. Here’s how to set both up.
What is Docker?
Docker runs applications in containers. A container bundles an app with everything it needs (libraries, runtime, config) so it runs the same way on any machine, isolated from everything else on the server.

Why that matters for a home server:
- No dependency hell: one app needing PHP 8.3 and another needing an older version doesn’t matter, because each brings its own.
- Easy to try things: spin an app up, and if you don’t like it, delete the container and it’s gone without leaving any mess behind.
- Everything is a text file: with Docker Compose, each app is described in one
compose.yaml, so rebuilding a server is mostly copying folders back.
Part 1: Installing Docker
These steps are for Ubuntu or Debian. Docker’s official install docs cover other distros.
1. Install Docker Engine and Compose
Docker’s convenience script adds their official repository and installs the engine plus the Compose plugin in one go:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
Prefer not to pipe scripts from the internet? The docs also show the manual apt repository method, which ends up in the same place.
2. Run Docker without sudo
sudo usermod -aG docker $USER
# log out and back in, then:
docker run hello-world
docker compose version
Security If hello-world prints its welcome message, Docker is working. One caution: membership of the docker group is effectively root access to the machine, so only add accounts you trust.
3. Pick a home for your stacks
I keep one folder per app under /opt/stacks, each with its own compose.yaml and data folders. It makes backups and moves simple:
/opt/stacks/
├── immich/compose.yaml
├── portainer/compose.yaml
├── vaultwarden/compose.yaml
└── wordpress/compose.yaml
sudo mkdir -p /opt/stacks
sudo chown $USER:docker /opt/stacks
What is Portainer?
Portainer is a web dashboard for Docker. It doesn’t replace Docker; it’s a friendly interface on top of the same engine you use from the command line. It runs as a container itself and talks to Docker through the Docker socket.

What I actually use it for:
- At-a-glance status: which containers are running, stopped or unhealthy.
- Logs and console: read a container’s logs or open a shell inside it from the browser, which is handy from a laptop or phone.
- Quick actions: start, stop, restart, or recreate a container with a fresh image.
- Housekeeping: finding and removing unused images and volumes that slowly eat disk space.
- Stacks: paste a compose file into the web editor and deploy it, without touching the terminal.
Part 2: Installing Portainer
1. Create the compose file
This is the exact file I run, saved as /opt/stacks/portainer/compose.yaml:
services:
portainer:
container_name: portainer
image: portainer/portainer-ce:lts
restart: unless-stopped
ports:
- "9443:9443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
volumes:
portainer_data:
name: portainer_data
portainer-ce:ltsis the free Community Edition, on the long-term-support release for fewer surprises.9443is Portainer’s HTTPS web interface.- The
docker.sockmount is what lets Portainer control Docker. portainer_datastores Portainer’s own settings and users.
2. Start it
cd /opt/stacks/portainer
docker compose up -d
3. Create your admin account (quickly!)
Browse to https://<your-server-ip>:9443. Your browser will warn about the certificate, because Portainer uses a self-signed one, so accept it and continue. Create the admin user straight away: for security, Portainer locks the setup screen if no admin is created within a few minutes. If that happens, run docker restart portainer and try again.
4. Connect the local environment
Choose Get Started to use the local Docker environment. You’ll see every container on the machine, including ones you started from the command line. Stacks deployed with docker compose show up too. Portainer marks them as created outside Portainer, with limited control, which suits me fine: I keep the compose files in /opt/stacks as the source of truth and use Portainer for monitoring, logs and restarts.
A word on security
Security Anything with access to docker.sock can control every container, and through them the whole host. So I don’t expose Portainer to the internet, even though everything else goes through my reverse proxy. It’s only reachable on my home network (or over a VPN when I’m away). Use a strong password, and turn on two-factor authentication if you’re on a Portainer edition that supports it.
What next?
With Docker and Portainer running, you’re ready to self-host just about anything. A great first project is replacing Google Photos with Immich.

Leave a Reply