DEV Community

Daniel Glover
Daniel Glover

Posted on • Originally published at danieljamesglover.com

Docker Compose Self-Hosted Services Guide

This article was originally published on danieljamesglover.com.


There is a certain satisfaction in running your own stack. Not because self-hosting is always the right choice, but because the discipline of deploying, securing, and maintaining your own services teaches you things that clicking through cloud consoles never does. You understand what a reverse proxy is doing when you have configured one. You understand secrets management when you have broken something by leaving credentials in a compose file.

I run a self-hosted stack at home and have built similar setups for small IT teams. This guide covers eight services worth running yourself, with working Docker Compose configurations, security notes, and an honest view of where self-hosting earns its keep versus where managed services are the right call.

Before you start, two things. First: Docker Compose is the right tool here. Not Kubernetes, not Nomad, not whatever the current trend is. For a small team or a serious home lab, Compose gives you readable declarative configuration, simple rollback, and low operational overhead. Second: put these services on a segmented network.

The Foundations Before the Services

Every service in this list shares a common infrastructure requirement: a reverse proxy. Running each service on a different host port (:8080, :8443, :3000) is fine for development, but it is a maintenance problem at any scale. You end up with port-mapping spreadsheets, inconsistent TLS handling, and no central place to manage access.

Traefik solves this. It is a container-aware reverse proxy that integrates directly with Docker and manages TLS certificates automatically via Let's Encrypt.

# traefik/docker-compose.yml
services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    command:
      - "--api.insecure=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - proxy

networks:
  proxy:
    external: true
Enter fullscreen mode Exit fullscreen mode

Create the proxy network once with docker network create proxy, then every service joins it and gets a Traefik label for routing.

1. Portainer - Container Management

Portainer gives you a browser-based view of running containers, volumes, networks, and compose stacks.

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - portainer_data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portainer.rule=Host(`portainer.yourdomain.com`)"
      - "traefik.http.routers.portainer.entrypoints=websecure"
      - "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
    networks:
      - proxy
Enter fullscreen mode Exit fullscreen mode

Security note: Restrict Portainer to your management VLAN or VPN. The admin account has root-equivalent access to everything Docker can reach.

2. BookStack - Documentation and Knowledge Base

BookStack uses a Book/Chapter/Page hierarchy that maps well to how IT documentation actually works.

services:
  bookstack:
    image: lscr.io/linuxserver/bookstack:latest
    container_name: bookstack
    restart: unless-stopped
    environment:
      - APP_URL=https://docs.yourdomain.com
      - DB_HOST=bookstack-db
      - DB_PASS=${DB_PASS}
    volumes:
      - ./config:/config
    networks:
      - proxy
      - internal

  bookstack-db:
    image: mariadb:10.11
    networks:
      - internal
Enter fullscreen mode Exit fullscreen mode

Note the internal: true network for the database - the database container has no external access.

3. Vaultwarden - Password Management

Vaultwarden is a lightweight Bitwarden-compatible server. For a small team sharing infrastructure credentials, it is the right answer.

services:
  vaultwarden:
    image: vaultwarden/server:latest
    environment:
      - DOMAIN=https://vault.yourdomain.com
      - SIGNUPS_ALLOWED=false
      - ADMIN_TOKEN=${ADMIN_TOKEN}
    volumes:
      - ./vw-data:/data
Enter fullscreen mode Exit fullscreen mode

SIGNUPS_ALLOWED=false is essential. After creating the accounts you need, disable open registration entirely.

4. Uptime Kuma - Monitoring

Uptime Kuma monitors URLs, TCP ports, Docker containers, and DNS entries, and sends alerts via Telegram, Slack, email, and a dozen other channels.

services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    volumes:
      - uptime-kuma:/app/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.uptime-kuma.rule=Host(`status.yourdomain.com`)"
Enter fullscreen mode Exit fullscreen mode

5. Gitea - Self-Hosted Git

Gitea is lightweight, fast, and has a GitHub-like interface. The whole thing runs on less than 256MB RAM.

services:
  gitea:
    image: gitea/gitea:latest
    environment:
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=gitea-db:5432
    networks:
      - proxy
      - internal

  gitea-db:
    image: postgres:15
    networks:
      - internal
Enter fullscreen mode Exit fullscreen mode

Security note: Disable public registration after creating your account.

6. Grafana with Prometheus - Metrics and Dashboards

Grafana and Prometheus give you time-series metrics, customisable dashboards, and alerting based on thresholds rather than binary up/down status.

services:
  prometheus:
    image: prom/prometheus:latest
    networks:
      - internal

  grafana:
    image: grafana/grafana:latest
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASS}
      - GF_USERS_ALLOW_SIGN_UP=false
    networks:
      - proxy
      - internal
Enter fullscreen mode Exit fullscreen mode

Prometheus is on the internal network only. Only Grafana is exposed via Traefik.

7. Nextcloud - File Sync and Collaboration

For a small IT team, the value is control: your files stay on your hardware, you set the retention policy, and you know exactly who has access to what.

8. Homepage - Service Dashboard

When you are running eight services, you want a single place to see them all. Homepage is a customisable application dashboard that integrates with Docker to pull running container status automatically.

The Security Baseline That Actually Matters

  • Secrets stay out of compose files. Use a .env file. Add .env to .gitignore immediately.
  • Separate networks by trust level. Public-facing services on the proxy network. Database containers on internal-only networks.
  • Run scheduled security scanning. Trivy and Docker Bench for Security are worth running regularly.
  • Back up volumes, not just images. The image is replaceable. Your BookStack content and Vaultwarden data are not.
  • Pin image versions in production. latest can introduce breaking changes on pull.

Read the full guide with complete compose configurations at danieljamesglover.com

Top comments (0)