Compare commits

..

No commits in common. "abdd6c130eda51c5cd0f0448e1a14acb9a707edf" and "da3aa5fa0d2df477b111e331030e64cf47f2f541" have entirely different histories.

23 changed files with 303 additions and 190 deletions

View File

@ -3,17 +3,14 @@
**DISCLAIMER**: this is still a *huge* work in progress.
### Goal
This repository aims to have a *small stack* of self hosted programs that are accessible through a single endpoint, the reverse proxy (Caddy) in the `caddy-docker-proxy` folder, that exposes whatever pieces of the stack you decide to have accessible from the outside, with or without using a domain.
This repository aims to have a *small stack* of self hosted programs that are accessible through a single endpoint, the reverse proxy (Nginx) in the `rp` folder, that exposes whatever pieces of the stack you decide to have accessible from the outside, with or without using a domain.
### Why not Docker Swarm? Or k8s?
As clearly explained on the [Docker Swarm Rocks](https://dockerswarm.rocks/swarm-or-kubernetes/) website, Docker Swarm Mode feels a bit left behind. I tried myself to build my stack on top of it, but it didn't feel as refined as the plain Docker itself. I'm still keeping an eye on the [Awesome Swarm](https://github.com/BretFisher/awesome-swarm) repository in case some new interesting tools come up.
Kubernetes has simply too much overhead for a small home lab like mine. I'm using a couple of air-gapped ARM64 boards, some mini PCs and a small Cloud VPS to achieve my needs, and at the time of writing k8s would add too much complexity to the stack.
The only thing that would make me change idea would be a need for autoscaling, but I'm still far from that situation.
That's something else in the plans, but this was more an attempt to answer the question: "what if I have a single machine but I want some modularity, without having to think too much when I want to add something?". I could still use Swarm or k8s on a single machine, but I find this solution a bit more suitable. This is also the reason I choose the Jwilder Nginx docker image over Traefik, as I didn't need service discovery on other nodes.
### How do I use this?
The `caddy-docker-proxy` is the first container that should be started, after running `$ docker network create caddy` to ensure the external network exists on the system. The `Caddyfile` included and mounted in `/etc/caddy/Caddyfile` is used in this case to give access to the air-gapped comtainers running on different machines on the same network. An example is found in the [Caddyfile](https://gitea.oddone.dev/Doddophonique/compose-personal-stack/src/branch/caddy-revamp/caddy-docker-proxy/Caddyfile), where the [Memos](https://gitea.oddone.dev/Doddophonique/compose-personal-stack/src/branch/caddy-revamp/memos/docker-compose.yml) container is exposed.
As you can see, this is a borderline situation where some people may prefer having service discovery with either Swarm or Kubernetes, but in my experience this is still not enough to call for that.
~~Nice question.~~
The `rp` folder is the first piece of the puzzle. It creates the proxy, the letsencrypt companion and the `rp_reverse-proxy` network that containers exposed to the internet will have to access. Every service in the Compose files tries to have the least amount of networks necessary to operate.
### Conclusion (for now):
This approach worked without any major issue in the last years, and it has been reliable for many projects that I will add to this repository. Maybe someone else can find it useful for their projects, and if so I'm happy for you. I'll make sure to link as many references I followed as I can inside the individual Compose files.
Although I still don't know if this approach has some major flaw(s), it has been reliable for many projects that I will add to this repository. Maybe someone else can find it useful for their projects, and if so I'm happy for you. I'll make sure to link as many references I followed as I can inside the individual Compose files.

View File

@ -1,21 +0,0 @@
# https://caddyserver.com/docs/metrics
:9000 {
metrics
}
subdomain.domain.tld {
reverse_proxy ${local_ip}:${port}
# https://caddyserver.com/docs/caddyfile/directives/basic_auth
basic_auth {
Username hashed_password
}
log
}
# This is an example based on the docker-compose.yml contained
# in the memos folder
memos.domain.tld {
reverse_proxy 192.168.1.128:9000
encode zstd gzip
log
}

View File

@ -1,59 +0,0 @@
---
x-logging:
&default-logging
driver: syslog
options:
# This requires two files in /etc/rsyslog.d
# https://www.loggly.com/use-cases/docker-syslog-logging-and-troubleshooting/
tag: "container_name/{{.Name}}"
labels: "${hostname}"
syslog-facility: # cron, local7, etc.
# Can be removed if not needed
x-opt-values:
&volume-opt
driver_opts: &options
type: "nfs"
o: "addr=${IP},rw"
services:
caddy:
image: lucaslorentz/caddy-docker-proxy:ci-alpine
container_name: caddy
restart: unless-stopped
labels:
caddy.email: ${CADDY_EMAIL}
environment:
# $ docker network create caddy
CADDY_INGRESS_NETWORKS: caddy
CADDY_DOCKER_CADDYFILE_PATH: "/etc/caddy/Caddyfile"
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- caddy_data:/data
- ./Caddyfile:/etc/caddy/Caddyfile
networks:
- caddy
ulimits:
nofile: 65535
logging: *default-logging
networks:
caddy:
external: true
volumes:
caddy_data: {}
# What if I want to use the x-opt-values:
# acme:
# <<: *volume-opt
# driver_opts:
# <<: *options
# device: ":/mnt/path/nginx-proxy/acme"
# o: "new_opts"
# type: "new_type"
#
# 'o' and 'type' can be redifined again or left at default

View File

@ -4,7 +4,7 @@ x-logging:
driver: syslog
options:
tag: "container_name/{{.Name}}"
labels: "LABEL"
labels: "q920"
syslog-facility: local7
x-opt-values:

View File

@ -1,53 +0,0 @@
---
x-logging:
&default-logging
driver: syslog
options:
tag: "container_name/{{.Name}}"
labels: "LABEL"
syslog-facility: local7
x-opt-values:
&volume-opt
driver_opts: &options
type: "nfs"
o: "addr=${IP},rw"
services:
memos:
image: neosmemo/memos:stable
container_name: memos
# Use labels if this container is hosted on the same machine as
# the Caddy reverse proxy
labels:
caddy: ${MEMOS_HOSTNAME}
caddy.log:
caddy.encode: "zstd gzip"
caddy.reverse_proxy: "{{upstreams 5230}}"
restart: unless-stopped
# This is not needed if labels are used. If this container is
# hosted on a different machine under the same subnetwoork, use
# its private IP address instead of 192.168.1.128. Check the
# Caddyfile inside caddy-docker-proxy folder for an example
# on how to forward traffic to this container
#
# ports:
# - "192.168.1.128:9000:5230"
volumes:
- /mnt/path:/var/opt/memos
networks:
- caddy
logging: *default-logging
# Optional, needed if you are using an NFS server
#
# volumes:
# volume:
# <<: *volume-opt
# driver_opts:
# <<: *options
# device: ":/mnt/path"
networks:
caddy:
external: true

View File

@ -1,13 +1,9 @@
---
x-logging:
&default-logging
driver: syslog
x-logging: &default-logging
driver: local
options:
# This requires two files in /etc/rsyslog.d
# https://www.loggly.com/use-cases/docker-syslog-logging-and-troubleshooting/
tag: "container_name/{{.Name}}"
labels: "${hostname}"
syslog-facility: # cron, local7, etc.
max-size: "1m"
max-file: "50"
x-opt-values: &volume-opt
driver_opts: &options
@ -29,74 +25,51 @@ services:
security_opt:
- apparmor:unconfined
volumes:
- ./netdataconfig:/etc/netdata:rw
- netdataconfig:/etc/netdata:ro
- netdatalib:/var/lib/netdata
- netdatacache:/var/cache/netdata
- /etc/passwd:/host/etc/passwd:ro
- /etc/group:/host/etc/group:ro
# - /dev:/host/dev:ro
# - /mnt:/host/mns:ro
# - /var/log:/host/var/log:ro
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /etc/os-release:/host/etc/os-release:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
labels:
caddy: ${NETDATA_HOSTNAME}
caddy.log:
caddy.encode: "zstd gzip"
caddy.reverse_proxy: "{{upstreams 19999}}"
caddy.basicauth.{USERNAME}: "hashed_password"
networks:
- netdata
- caddy
- ngp
- reverse-proxy
- npg
logging: *default-logging
prometheus:
image: prom/prometheus
container_name: prometheus
hostname: prometheus
# Only if you need to access it locally
# ports:
# - "9090:9090"
env_file:
prometheus.env
volumes:
- prometheus:/etc/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=180d'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
# - '--web.enable-admin-api'
# - '--web.enable-remote-write-receiver'
networks:
- ngp
- caddy
networks:
- npg
logging: *default-logging
grafana:
image: grafana/grafana
container_name: grafana
restart: unless-stopped
env_file:
grafana.env
volumes:
- grafana:/var/lib/grafana
labels:
caddy: grafana.domain.tld
caddy.log:
caddy.reverse_proxy: "{{upstreams 3000}}"
networks:
- ngp
- caddy
- npg
logging: *default-logging
# Can be substituted by a standard volume declaration if it's just a test
# environment or no nfs server is available
volumes:
netdataconfig:
<<: *volume-opt
driver_opts:
<<: *options
device: ":/mnt/path/netdata/netdataconfig"
netdatalib:
<<: *volume-opt
driver_opts:
@ -119,7 +92,7 @@ volumes:
device: ":/mnt/path/grafana/grafana"
networks:
netdata:
ngp:
caddy:
reverse-proxy:
name: rp_reverse-proxy
external: true
npg:

View File

@ -1 +1,3 @@
VIRTUAL_HOST="grafana.domain.tld"
VIRTUAL_PORT=3000
LETSENCRYPT_HOST="grafana.domain.tld"

View File

@ -1,3 +1,6 @@
NETDATA_CLAIM_TOKEN=${TOKEN}
VIRTUAL_HOST="netdata.domain.tld"
VIRTUAL_PORT=19999
LETSENCRYPT_HOST="netdata.domain.tld"
NETDATA_CLAIM_TOKEN=hOOww34Yc-6vYBYxRKBop6Ozbsf0B2WSaPBTsW_mbHuBYiHcGDVsvbqETz5SaevMvOVBK_4r7q6WwLZTTD3iwHy2T2X1NsRExqUWWUn3LQXDGYlvKKCynVRafuNT9xmH1cuQ0l8
NETDATA_CLAIM_URL=https://app.netdata.cloud
NETDATA_CLAIM_ROOMS=

View File

@ -0,0 +1,5 @@
# File that has to be put under /etc/netdata/go.d/nginx.conf to monitor
# nginx_status
jobs:
- name: local
url: http://nginx-proxy/nginx_status

View File

@ -1 +1,3 @@
VIRTUAL_HOST="prometheus.domain.tld"
VIRTUAL_PORT=9090
LETSENCRYPT_HOST="prometheus.domain.tld"

1
pmb/.env.template Normal file
View File

@ -0,0 +1 @@
COMPOSE_PROJECT_NAME=pmb

1
pmb/README.md Normal file
View File

@ -0,0 +1 @@

33
pmb/docker-compose.yml Normal file
View File

@ -0,0 +1,33 @@
x-logging:
&default-logging
driver: local
options:
max-size: "1m"
max-file: "50"
x-opt-values:
&volume-opt
driver_opts: &options
type: "nfs"
o: "addr=${IP},rw"
services:
protonmail-bridge:
image: shenxn/protonmail-bridge
container_name: protonmail-bridge
restart: always
volumes:
- protonmail:/root
networks:
- protonmail
logging: *default-logging
volumes:
protonmail:
<<: *volume-opt
driver_opts:
<<: *options
device: ":/mnt/path/protonmail/protonmail"
networks:
protonmail:

3
rp/.env.template Normal file
View File

@ -0,0 +1,3 @@
# Simple reverse proxy project name to simplify network referencing.
COMPOSE_PROJECT_NAME=rp
DEFAULT_EMAIL=""

1
rp/README.md Normal file
View File

@ -0,0 +1 @@

View File

@ -0,0 +1,12 @@
# File to place in the conf/ directory of Nginx, to give other containers the
# capability of accessing Nginx status
server {
listen ${proxy-container-name}:80;
server_name ${proxy-container-name};
location /nginx_status {
stub_status on;
allow all;
access_log on;
}
}

78
rp/docker-compose.yml Normal file
View File

@ -0,0 +1,78 @@
x-logging:
&default-logging
driver: syslog
options:
# This requires two files in /etc/rsyslog.d
# https://www.loggly.com/use-cases/docker-syslog-logging-and-troubleshooting/
tag: "container_name/{{.Name}}"
labels: "${hostname}"
syslog-facility: # cron, local7, etc.
# Can be removed if not needed
x-opt-values:
&volume-opt
driver_opts: &options
type: "nfs"
o: "addr=${IP},rw"
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: proxy
labels:
com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true"
ports:
- "80:80"
- "443:443"
restart: always
volumes:
- conf:/etc/nginx/conf.d
- vhost:/etc/nginx/vhost.d
- passwords:/etc/nginx/htpasswd
- html:/usr/share/nginx/html
- certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
- reverse-proxy
logging: *default-logging
letsencrypt:
image: nginxproxy/acme-companion
container_name: letsencrypt
restart: always
volumes:
- conf:/etc/nginx/conf.d
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- certs:/etc/nginx/certs:rw
- acme:/etc/acme.sh
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
NGINX_PROXY_CONTAINER: proxy
DEFAULT_EMAIL: ${DEFAULT_EMAIL}
networks:
- reverse-proxy
logging: *default-logging
networks:
reverse-proxy:
volumes:
acme:
certs:
conf:
html:
passwords:
vhost:
# What if I want to use the x-opt-values:
# acme:
# <<: *volume-opt
# driver_opts:
# <<: *options
# device: ":/mnt/path/nginx-proxy/acme"
# o: "new_opts"
# type: "new_type"
#
# 'o' and 'type' can be redifined again or left at default

View File

@ -0,0 +1,6 @@
# https://learn.netdata.cloud/docs/agent/running-behind-nginx#enable-authentication
# Putting username and hashed password inside the htpasswd folder of
# jwilder/nginx-proxy activates the Basic auth for the domain you
# use as the filename of this file.
${username}:${hashed-password}

View File

@ -0,0 +1,10 @@
# https://docs.firefly-iii.org/firefly-iii/installation/docker/
# In the section "Docker and reverse proxies", this is suggested for Nginx.
# Put it in the vhost.d/ folder, with your domain and _location at the end
# as a filename.
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;

View File

@ -0,0 +1,16 @@
# https://learn.netdata.cloud/docs/agent/running-behind-nginx#ways-to-access-netdata-via-nginx
# Content suggested by Netdata documentation, excluding declarations that will
# be automatically populated by jwilder/nginx-proxy.
# To be placed in the vhost.d folder of Nginx, remember to put your domain as
# the filename with _location at the end.
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_headers on;
proxy_set_header Connection "keep-alive";
proxy_store off;
gzip on;
gzip_proxied any;
gzip_types *;

View File

@ -0,0 +1,16 @@
# Template file for the routes of Vaultwarden. To be placed in the vhost.d/
# folder of Nginx, substitute the name of the file with your domain.
location /admin {
return 404;
}
location /notifications/hub {
proxy_pass http://${vaultwarden-container-name}:3012;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /notifications/hub/negotiate {
proxy_pass http://${vaultwarden-container-name}:8080;
}

View File

@ -0,0 +1,9 @@
COMPOSE_PROJECT_NAME=vaultwarden
TOKEN="YOUR_TOKEN_HERE"
EXPOSED_PORT=8080
NDOMAIN="warden.domain.tld"
VDOMAIN="https://warden.domain.tld"
SFROM="mail@domain.tld"
SFROMNAME="Your Name"
SUSER="" # User login for Protonmail Bridge
SPASS="" # Password of the user (retrieved from inside the Protonmail Bridge container)

View File

@ -0,0 +1,78 @@
x-logging:
&default-logging
driver: syslog
options:
# This requires two files in /etc/rsyslog.d
# https://www.loggly.com/use-cases/docker-syslog-logging-and-troubleshooting/
tag: "container_name/{{.Name}}"
labels: "${hostname}"
syslog-facility: # cron, local7, etc.
# Can be removed if not needed
x-opt-values:
&volume-opt
driver_opts: &options
type: "nfs"
o: "addr=${IP},rw"
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: always
environment:
ADMIN_TOKEN: ${TOKEN} # Set token if you want the admin page available
ROCKET_PORT: ${EXPOSED_PORT}
VIRTUAL_PORT: ${EXPOSED_PORT} # Used by nginx-proxy
VIRTUAL_HOST: ${NDOMAIN} # Used by nginx-proxy
LETSENCRYPT_HOST: ${NDOMAIN}
DOMAIN: ${VDOMAIN} # Used by vaultwarden to set certain links
WEBSOCKET_ENABLED: "true"
SIGNUPS_ALLOWED: "false" # Change to true if it's the first time running
# Optional environment, but useful if you want some functions
SMTP_HOST: "${protonmail-container-name}"
SMTP_FROM: ${SFROM}
SMTP_FROM_NAME: ${SFROMNAME}
SMTP_PORT: "25" # Default SMTP port for Protonmail Bridge
SMTP_USERNAME: ${SUSER}
SMTP_PASSWORD: ${SPASS}
SMTP_ACCEPT_INVALID_CERTS: "true" # Necessary when using Protonmail Bridge
volumes:
- vw-data:/data
networks:
- reverse-proxy
- vaultwarden
- protonmail
vaultwarden-backup:
image: bruceforce/vaultwarden-backup
container_name: vaultwarden-backup
restart: always
environment:
TIMESTAMP: "true"
UID: ${UID}
GID: ${GID}
BACKUP_DIR: ${BACKUP_DIR}
DELETE_AFTER: "30"
CRON_TIME: "0 2 * * *"
volumes:
- vw-data:/data
- backup:/backup
volumes:
vw-data:
# This stores the backup on a (possibly) remote server
backup:
<<: *volume-opt
driver_opts:
<<: *options
device: ":/mnt/path/vaultwarden/backup"
networks:
reverse-proxy:
name: rp_reverse-proxy
external: true
vaultwarden:
protonmail:
name: pmb_protonmail
external: true