10 Best Docker Container Stacks I am using in 2024
I have been using tons of Docker stacks this year and moving into 2025, that doesn’t look to be slowing down for me. I have several standalone Docker container hosts, as well as a production swarm cluster in the home lab that I have been running for some months now that is extremely awesome, with tools like Portainer. Check out my write up showing how Docker Swarm is awesome with Portainer and how you can build the exact same thing as I am using. I have been committed to checking in my docker compose code to my repo to have proper versioning of my code and keep up with things much better. Let’s look at the 10 best docker container stacks I am using in the lab at the end of 2024.
Table of contents
What are docker container stacks?
A docker “stack” is kind of a general term that many different tools use to describe a way to group multiple containers together in a “stack” to work as one application. Docker-compose makes this possible and is really the reason behind it. You can bring up your docker container stack as one unit, even if it is made up of 8, 10 or multiple containers.
I have been organizing my docker container stacks in a repository this year in a self-hosted repository in GitLab and keeping up with the stacks I am using in a much more organized way. Let’s now look at the stacks that I have on my 10 best docker container stacks I am using in 2024.
1. Nginx Proxy Manager
The docker container stack that I want to start with is what I have built my docker container environment around, Nginx Proxy Manager (NPM). With NPM, I create the network that I use for the other container stacks in my environment. I like to build my other stacks around the proxy service that I am using as I have found this seems to work out well and leads to consistency from a networking perspective.
This way I can force all traffic to go through the NPM proxy function and the network it provisions. For my environment, NPM provides SSL termination for my containerized solutions in the home lab and I love it. Traefik is awesome to and provides a solution that is well-suited for either Docker or Kubernetes environments. But, NPM is just very straightforward and easy with the GUI. If you need to provide an easy way to add SSL certificates to your environment, NPM is a great choice.
version: '3.8'
services:
npm:
image: 'jc21/nginx-proxy-manager:latest'
restart: always
networks:
- nginxproxy
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- "/home/linuxadmin/homelabservices/nginxproxymanager/data:/data"
- "/home/linuxadmin/homelabservices/nginxproxymanager/letsencrypt:/etc/letsencrypt"
container_name: nginxproxy
npmdb:
image: 'jc21/mariadb-aria:latest'
restart: always
networks:
- nginxproxy
environment:
MYSQL_ROOT_PASSWORD: 'npm'
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npm'
volumes:
- /home/linuxadmin/homelabservices/nginxproxymanager/mysql:/var/lib/mysql
networks:
nginxproxy:
driver: bridge
name: nginxproxy
2. GitLab
I have tried other self-hosted code repos and CI/CD solutions and still keep coming back to GitLab. I think it is the best one out there with the most mature tools and capabilities. If you want a single solution that can do everything in a seamless way, I think GitLab is my top pick here.
There are many other great self-hosted solutions like Gitea that I like very much and am still trying out and playing around with. But, for my tastes currently, the the CI/CD capabilities with Gitea are still a bit immature. Here is my GitLab stack for running Gitlab and a runner:
version: '3.8'
services:
gitlab:
image: gitlab/gitlab-ee:latest
hostname: 'gitlab.mydomain.com'
restart: always
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://gitlab.mydomain.com'
letsencrypt['enabled'] = false
# Reverse proxy nginx config
nginx['listen_port'] = 80
nginx['listen_https'] = false
nginx['redirect_http_to_https'] = false
nginx['proxy_set_headers'] = {
"X-Forwarded-Proto" => "https",
"X-Forwarded-Ssl" => "on",
"Host" => "gitlab.mydomain.com",
"X-Real-IP" => "$$remote_addr",
"X-Forwarded-For" => "$$proxy_add_x_forwarded_for",
"Upgrade" => "$$http_upgrade",
"Connection" => "$$connection_upgrade"
}
networks:
- nginxproxy
volumes:
- '/home/linuxadmin/homelabservices/gitlab/data:/var/opt/gitlab'
- '/home/linuxadmin/homelabservices/gitlab/config:/etc/gitlab'
- '/home/linuxadmin/homelabservices/gitlab/logs:/var/log/gitlab'
container_name: gitlab
gitlab-runner:
image: gitlab/gitlab-runner:latest
restart: always
networks:
- nginxproxy
volumes:
- '/home/linuxadmin/homelabservices/gitlab-runner/config:/etc/gitlab-runner'
- '/var/run/docker.sock:/var/run/docker.sock'
container_name: gitlab_runner
networks:
nginxproxy:
external: true
3. phpIPAM
Another docker container stack that I think is excellent, especially for home lab environments is phpIPAM. With phpIPAM you can have a solution to kee track of ip addressing in your environment. With phpIPAM you can continuously scan your network for new IP addresses and discover hosts and addresses in the environment
version: '3.8'
services:
phpipam-web:
image: phpipam/phpipam-www:latest
ports:
- "8100:80"
environment:
- TZ=America/Chicago
- IPAM_DATABASE_HOST=phpipam-mariadb
- IPAM_DATABASE_PASS=password
- IPAM_DATABASE_WEBHOST=%
- IPAM_TRUST_X_FORWARDED=true
restart: always
volumes:
- "/home/linuxadmin/homelabservices/phpipam/phpipam-logo:/phpipam/css/images/logo"
- "/home/linuxadmin/homelabservices/phpipam/phpipam-ca:/usr/local/share/ca-certificates:ro"
networks:
- nginxproxy
depends_on:
- phpipam-mariadb
container_name: phpipam-web
phpipam-cron:
image: phpipam/phpipam-cron:latest
environment:
- TZ=America/Chicago
- IPAM_DATABASE_HOST=phpipam-mariadb
- IPAM_DATABASE_PASS=password
- SCAN_INTERVAL=1h
restart: always
volumes:
- "/home/linuxadmin/homelabservices/phpipam/phpipam-ca:/usr/local/share/ca-certificates:ro"
networks:
- nginxproxy
depends_on:
- phpipam-mariadb
command: |
sh -c "echo '*/15 * * * * /usr/bin/php /phpipam/functions/scripts/pingCheck.php' >> /etc/crontabs/root &&
echo '*/15 * * * * /usr/bin/php /phpipam/functions/scripts/discoveryCheck.php' >> /etc/crontabs/root &&
crond -f"
container_name: phpipam-cron
phpipam-mariadb:
image: mariadb:latest
environment:
- MYSQL_ROOT_PASSWORD=password
restart: always
volumes:
- "/home/linuxadmin/homelabservices/phpipam/phpipam-db-data:/var/lib/mysql"
networks:
- nginxproxy
container_name: phpipam-mariadb
networks:
nginxproxy:
external: true
4. Watchtower
The Watchtower docker container stack is a great way to keep your containers updated across the board. When you run Watchtower, it watches all the other containers and makes sure these stay updated if there is a new container image released. Also, Watchtower will notify the appropriate parties if there is an update that is processed for a container image.
version: '3.8'
services:
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: always
networks:
- nginxproxy
environment:
WATCHTOWER_SCHEDULE: "0 0 1 * * *"
TZ: America/Chicago
WATCHTOWER_CLEANUP: "true"
WATCHTOWER_DEBUG: "true"
WATCHTOWER_NOTIFICATIONS: "email"
WATCHTOWER_NOTIFICATION_EMAIL_FROM: "[email protected]"
WATCHTOWER_NOTIFICATION_EMAIL_TO: "[email protected]"
# you have to use a network alias here, if you use your own certificate
WATCHTOWER_NOTIFICATION_EMAIL_SERVER: "10.1.149.19"
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT: "8025"
WATCHTOWER_NOTIFICATION_EMAIL_DELAY: 2
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
nginxproxy:
external: true
5. Container monitoring stack
The container monitoring stack is a great way to keep a check on your containers and their performance, health, and other metrics. This includes prometheus, cadvisor, and node exporter.
services:
prometheus:
image: portainer/template-swarm-monitoring:prometheus-v2.44.0
user: "1001"
environment:
- PUID=1001
- PGID=1001
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--log.level=error'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=7d'
volumes:
- /mnt/cephfs/prometheus:/prometheus
- /mnt/cephfs/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
networks:
- nginxproxy
restart: unless-stopped
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.47.0
command: -logtostderr -docker_only
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk:/dev/disk:ro
networks:
- nginxproxy
restart: unless-stopped
node-exporter:
image: prom/node-exporter:v1.5.0
command:
- '--path.sysfs=/host/sys'
- '--path.procfs=/host/proc'
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
- '--no-collector.ipvs'
volumes:
- /:/rootfs:ro
- /proc:/host/proc:ro
- /sys:/host/sys:ro
networks:
- nginxproxy
restart: unless-stopped
networks:
nginxproxy:
external: true
6. TICK Stack container stack
TICK stack represents Telegraph, InfluxDB, Chronograf, and Kapacitor containers. This is a great stack of containers also for monitoring that can be used and is quite popular for this use case.
version: '3.8'
services:
grafana:
image: grafana/grafana
ports:
- 3000:3000
environment:
- GF_PANELS_DISABLE_SANITIZE_HTML=true
networks:
- nginxproxy
volumes:
- /mnt/cephfs/grafana/grafana-volume:/var/lib/grafana
restart: unless-stopped
influxdb:
image: influxdb:latest
ports:
- 8086:8086
- 8089:8089/udp
networks:
- nginxproxy
volumes:
- /mnt/cephfs/influxdb/influxdb-volume:/var/lib/influxdb2
restart: unless-stopped
chronograf:
user: "1000"
image: chronograf:latest
hostname: chronograf
ports:
- 8888:8888
networks:
- nginxproxy
volumes:
- /mnt/cephfs/chronograf/chronograf-data:/var/lib/chronograf
restart: unless-stopped
telegraf:
image: telegraf:latest
hostname: telegraf
networks:
- nginxproxy
volumes:
- /mnt/cephfs/telegraf/etc/telegraf.conf:/etc/telegraf/telegraf.conf
restart: unless-stopped
kapacitor:
user: "1000"
image: kapacitor:latest
hostname: kapacitor
networks:
- nginxproxy
volumes:
- /mnt/cephfs/kapacitor/kapacitor-data:/var/lib/kapacitor
- /mnt/cephfs/kapacitor/kapacitor-data/etc/kapacitor.conf:/etc/kapacitor/kapacitor.conf
restart: unless-stopped
networks:
nginxproxy:
external: true
7. Gitea
In addition to using GitLab, I have been testing Gitea as well and it looks like it is a great solution. I still prefer GitLab at the moment as I continue to learn the ins and out of Gitea, but it is a great solution that allows you to self-host a code repository and now Gitea also has Gitea actions that provides CI/CD capabilities.
version: "3.8"
services:
gitea:
image: gitea/gitea:latest
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=mysql
- GITEA__database__HOST=gitea-db:3306
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea
- GITEA__security__INSTALL_LOCK=true
- GITEA__security__SECRET_KEY=342bc7e3db20204ec9af2bb4ecbfa26688ad96d85fd82494e173b99b38fa2cf3
- GITEA__service__DISABLE_REGISTRATION=true
- GITEA__admin__USERNAME=admin
- GITEA__admin__PASSWORD=password
- [email protected]
- GITEA__mailer__ENABLED=true
- GITEA__mailer__SMTP_ADDR=10.1.149.19:8025
- [email protected]
- GITEA__mailer__PROTOCOL=mail
- GITEA__security__ALLOWED_DOMAINS=gitea.mydomain.com
- GITEA__security__ALLOW_LOCALNETWORKS=true
networks:
- nginxproxy
volumes:
- /mnt/cephfs/gitea/data:/data
deploy:
replicas: 1
restart_policy:
condition: on-failure
gitea-db:
image: mysql:8
environment:
- MYSQL_ROOT_PASSWORD=gitea
- MYSQL_USER=gitea
- MYSQL_PASSWORD=gitea
- MYSQL_DATABASE=gitea
networks:
- nginxproxy
volumes:
- /mnt/cephfs/gitea-db/mysql-data:/var/lib/mysql
deploy:
replicas: 1
restart_policy:
condition: on-failure
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s # Interval between health checks
timeout: 5s # Timeout for each health check attempt
retries: 5 # Number of retries before marking as unhealthy
start_period: 30s # Initial delay before starting health checks
gitea-runner:
image: gitea/act_runner:nightly
environment:
CONFIG_FILE: /config.yaml
GITEA_INSTANCE_URL: "gitea.mydomain.com}"
GITEA_RUNNER_REGISTRATION_TOKEN: "password"
GITEA_RUNNER_NAME: "gitearunnerdkr01"
GITEA_RUNNER_LABELS: "docker"
volumes:
- /mnt/cephfs/gitea-runner/config.yaml:/config.yaml
- /mnt/cephfs/gitea-runner/cache:/cache
- /mnt/cephfs/gitea-runner/data:/data
- /var/run/docker.sock:/var/run/docker.sock
networks:
nginxproxy:
external: true
8. Hashicorp Vault
Another great docker stack solution to mention is Hashicorp Vault. I use Vault with my Packer builds, terraform, and other use cases to securely store secrets in the environment. Using Vault, you can have a secure location for your secrets that can be dynamically pulled during CI/CD operations and other infrastructure automation.
version: "3.8"
services:
vault:
image: hashicorp/vault:latest
volumes:
- /mnt/cephfs/vault/config:/vault/config
- /mnt/cephfs/vault/data:/vault/file
networks:
- nginxproxy
cap_add:
- IPC_LOCK
command: "vault server -config=/vault/config/vault-config.json"
restart: unless-stopped
networks:
nnginxproxy:
external: true
9. Pi-Hole
If you are looking for a great all-in-one DNS solution that also has good security features, ad-blocking, and malware protection, Pi-Hole is a good place to start. I use it on my “family” side of the network as an additional protection against malware, etc.
version: '3.8'
services:
pihole:
image: pihole/pihole:latest
ports:
- "53:53/tcp"
- "53:53/udp"
dns:
- 127.0.0.1
- 1.1.1.1
environment:
TZ: 'America/Chicago'
WEBPASSWORD: 'password'
PIHOLE_DNS_: 1.1.1.1;9.9.9.9
DNSSEC: 'false'
VIRTUAL_HOST: pihole01.mydomain.com
WEBTHEME: default-dark
PIHOLE_DOMAIN: lan
volumes:
- /mnt/cephfs/pihole/pihole:/etc/pihole
- /mnt/cephfs/pihole/dnsmasq.d:/etc/dnsmasq.d
deploy:
restart_policy:
condition: any
delay: 5s
max_attempts: 3
networks:
- nginxproxy
networks:
nginxproxy:
external: true
10. Unifi network application
I imagine a lot of you are running Unifi in your network. You can easily self-host a docker stack for running your Unifi network application/controller in the environment. This allows you to self-host the controller in your network.
version: '3.8'
services:
unifi:
image: jacobalberty/unifi:latest
restart: always
networks:
- nginxproxy
volumes:
- /mnt/cephfs/unifi/data/lib:/var/lib/unifi
- /mnt/cephfs/unifi/data/log:/var/log/unifi
- /mnt/cephfs/unifi/data/run:/var/run/unifi
ports:
- '3478:3478/udp'
- '10001:10001/udp'
- '6789:6789/tcp'
- '8080:8080/tcp'
- '8880:8880/tcp'
- '8843:8843/tcp'
environment:
- TZ=America/Chicago
networks:
nginxproxy:
external: true
Wrapping up
Are these the only docker stacks you can deploy, of course not! But these are just 10 of the most handy docker stacks that I am using and working with in my home lab environment and that provide critical services for my needs in the environment. Check them out and see if these are ones that you can take advantage of as well. Let me know other solutions you might be using as well.