Modern setup for selfhosted services

2026-08-28
#howto #ubuntu #server #selfhosted #docker

§ Deploy stack

In this note I’ll document my homelab setup on one cloud VPS with docker-compose. I’m finally happy with the result.

For my homelab I use:

  • Glance as a home dashboard
  • Gitea: git to store code, configuration, etc.
  • Vaultwarden: password manager
  • Memos: small notes I use as public “twitter”
  • Miniflux: rss aggregator
  • Outline: wiki to store notes and documentation, neat integration with draw.io
  • 3x-ui and WireGuard for “corporate” network

The full docker-compose collection is available in the repo: https://git.ksar.dev/bikulov/ksar

I’d like to outline some features:

  1. only the proxy (Traefik) exposes web ports
  2. services with two or more containers are isolated by networks: for example, miniflux has no network access to outline-db
  3. variables in .env files, example for each service provided
  4. each service is a separate folder. You may use only traefik and gitea: just run those

Where needed, services are configured to support SSO and to connect to gitea as an SSO provider.

All services use SSO for login. But it is not configured initially. So deploy order is important:

  1. Start with traefik
  2. Then gitea as an SSO provider
  3. … then any of the supported services …
  4. Configure glance to monitor all services and start it

Below are some details about chosen settings.

restart: unless-stopped - all containers restart after a Docker daemon reload (if they are not stopped manually).

I prefer storing data in folders:

volumes:
  - ./data/gitea:/data

§ traefik settings explanation

  • --api.insecure=false – disable the insecure API and dashboard: necessary for production
  • --providers.docker=true – configure Traefik for Docker
  • --providers.docker.exposedbydefault=false – do not add all containers to traefik, only those explicitly marked with traefik.enable=true
  • --providers.docker.network=proxy – what network to use in Docker to choose the IP for the service

These three lines are for HTTP -> HTTPS redirect:

  • --entryPoints.web.address=:80
  • --entryPoints.web.http.redirections.entryPoint.to=websecure
  • --entryPoints.web.http.redirections.entryPoint.scheme=https

Settings for automatic Let’s Encrypt:

  • --entryPoints.websecure.address=:443
  • --certificatesresolvers.le.acme.tlschallenge=true
  • --certificatesresolvers.le.acme.email=${TRAEFIK_MAIL}
  • --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json

And the only labels needed for containers are:

  • traefik.enable=true
  • traefik.http.services.<CONTAINER_NAME>.loadbalancer.server.port=<APP_PORT>
  • traefik.http.routers.<CONTAINER_NAME>.rule=Host(${SERVER_HOST})
  • traefik.http.routers.<CONTAINER_NAME>.entrypoints=websecure
  • traefik.http.routers.<CONTAINER_NAME>.tls.certresolver=le

Happy homelabbing!