## Please email lab@seedno.de with any questions about this file, or setting up any services therein
## The actual files are not a monolith like this one; this page is generated as a convenient reference

## The following variables should be set in a file named .env in the same directory as docker-compose.yml (alter as needed):
# - CLOUDFLARE_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# - TIMEZONE=America/Chicago
# - UID=1000
# - GID=1000

## The firewall is configured as follows:

# Allow traffic from traefik to other containers
# $ sudo iptables -A DOCKER-USER -s 10.160.3.254/32 -d 10.160.0.0/22 -i br_traefik -o br_traefik -j ACCEPT

# Allow other containers to reply to traefik
# $ sudo iptables -A DOCKER-USER -s 10.160.0.0/22 -d 10.160.3.254/32 -i br_traefik -o br_traefik -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Block all other traffic between containers not sharing an internal network
# $ sudo iptables -A DOCKER-USER -i br_traefik -o br_traefik -j REJECT

version: "3.9"

services:

  ### https://github.com/traefik/traefik
  ## Files for basicauth middleware should be generated with the following:
  # $ htpasswd -c "${filename}" "${username}"
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    environment:
      - "CF_DNS_API_TOKEN=${CLOUDFLARE_API_TOKEN:?not set}"
      - "TZ=${TIMEZONE:?not set}"
    command:
      - "--accesslog=true"
      - "--accesslog.bufferingsize=100"
      - "--accesslog.fields.names.StartUTC=drop"
      - "--accesslog.filepath=/var/log/access.log"
      - "--api"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"
      - "--certificatesresolvers.letsencrypt.acme.storage=/certs/acme.json"
      - "--entrypoints.http.address=:80"
      - "--entrypoints.https.address=:443"
      - "--entrypoints.https.http3"
      - "--entrypoints.metrics.address=:8082"
      - "--experimental.http3=true"
      - "--log.filePath=/var/log/traefik.log"
      - "--log.level=ERROR"
      - "--metrics.prometheus=true"
      - "--metrics.prometheus.addentrypointslabels=true"
      - "--metrics.prometheus.addserviceslabels=true"
      - "--metrics.prometheus.entrypoint=metrics"
      - "--providers.docker=true"
      - "--providers.docker.exposedByDefault=false"
      - "--providers.docker.network=traefik"
      - "--providers.file.directory=/conf"
      - "--providers.file.watch=true"
      - "--serverstransport.insecureskipverify=true"
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.compress.compress=true"
      - "traefik.http.middlewares.errors.errors.status=404,502,504"
      - "traefik.http.middlewares.errors.errors.service=errors"
      - "traefik.http.middlewares.errors.errors.query=/{status}.html"
      - "traefik.http.middlewares.adminauth.basicauth.usersfile=/conf/auth/admin"
      - "traefik.http.middlewares.familyauth.basicauth.usersfile=/conf/auth/family"
      - "traefik.http.middlewares.friendsauth.basicauth.usersfile=/conf/auth/friends"
      - "traefik.http.middlewares.ratelimit.ratelimit.average=3"
      - "traefik.http.middlewares.ratelimit.ratelimit.burst=1"
      - "traefik.http.middlewares.ratelimit.ratelimit.period=1"
      - "traefik.http.middlewares.ratelimit.ratelimit.sourcecriterion.requestheadername=Cf-Connecting-Ip"
      - "traefik.http.middlewares.secure.headers.browserxssfilter=true"
      - "traefik.http.middlewares.secure.headers.contenttypenosniff=true"
      - "traefik.http.middlewares.secure.headers.customframeoptionsvalue=SAMEORIGIN"
      - "traefik.http.middlewares.secure.headers.customresponseheaders.Expect-CT=enforce,max-age=86400"
      - "traefik.http.middlewares.secure.headers.customresponseheaders.Permissions-Policy=geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(), payment=()"
      - "traefik.http.middlewares.secure.headers.customresponseheaders.Server=GNU-Netcat/0.7.1"
      - "traefik.http.middlewares.secure.headers.customresponseheaders.X-Clacks-Overhead=GNU Terry Pratchett"
      - "traefik.http.middlewares.secure.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.middlewares.secure.headers.forcestsheader=true"
      - "traefik.http.middlewares.secure.headers.framedeny=true"
      - "traefik.http.middlewares.secure.headers.referrerpolicy=strict-origin"
      - "traefik.http.middlewares.secure.headers.sslredirect=true"
      - "traefik.http.middlewares.secure.headers.stsincludesubdomains=true"
      - "traefik.http.middlewares.secure.headers.stspreload=true"
      - "traefik.http.middlewares.secure.headers.stsseconds=63072000"
      - "traefik.http.routers.traefik.rule=Host(`dashboard.crimson.seedno.de`)"
      - "traefik.http.routers.traefik.entrypoints=https"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.tls=true"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
      - "traefik.http.routers.traefik.middlewares=adminauth,errors"
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    networks:
      traefik:
        ipv4_address: 10.160.3.254
      prometheus:
    volumes:
      - type: bind
        source: /docker/traefik/certs
        target: /certs
      - type: bind
        source: /docker/traefik/config
        target: /conf
        read_only: true
      - type: bind
        source: /docker/traefik/logs
        target: /var/log
      - type: bind
        source: /var/run/docker.sock
        target: /var/run/docker.sock
        read_only: true

  ### https://github.com/ArchiveBox/ArchiveBox
  ## To add superuser:
  # $ docker exec -it archivebox /bin/sh -c 'su archivebox && cd /data && archivebox manage createsuperuser'
  ## To install additional dependencies:
  # $ docker exec -it archivebox /bin/sh -c 'su archivebox && cd /data && archivebox setup'
  archivebox:
    image: archivebox/archivebox:master
    container_name: archivebox
    restart: unless-stopped
    command: server --quick-init 0.0.0.0:8000
    depends_on:
      - archivebox-sonic
    environment:
      - "CHECK_SSL_VALIDITY=True"
      - "CHROME_HEADLESS=True"
      - "CHROME_SANDBOX=False"
      - "FOOTER_INFO=Contact archive@seedno.de with questions or concerns."
      - "IN_DOCKER=True"
      - "ONLY_NEW=False"
      - "OUTPUT_PERMISSIONS=775"
      - "PGID=${GID:?not set}"
      - "PUID=${UID:?not set}"
      - "PUBLIC_ADD_VIEW=False"
      - "PUBLIC_INDEX=False"
      - "PUBLIC_SNAPSHOTS=False"
      - "RESOLUTION=1920,1080"
      - "SAVE_ARCHIVE_DOT_ORG=False"
      - "SAVE_DOM=True"
      - "SAVE_FAVICON=True"
      - "SAVE_GIT=True"
      - "SAVE_HEADERS=True"
      - "SAVE_MEDIA=True"
      - "SAVE_MERCURY=True"
      - "SAVE_PDF=True"
      - "SAVE_READABILITY=True"
      - "SAVE_SCREENSHOT=True"
      - "SAVE_SINGLEFILE=True"
      - "SAVE_TITLE=True"
      - "SAVE_WARC=True"
      - "SAVE_WGET=True"
      - "SAVE_WGET_REQUISITES=True"
      - "SEARCH_BACKEND_ENGINE=sonic"
      - "SEARCH_BACKEND_HOST_NAME=sonic"
      - "SEARCH_BACKEND_PASSWORD=${ARCHIVEBOX_SEARCH_BACKEND_PASSWORD:?not set}"
      - "SHOW_PROGRESS=False"
      - "SNAPSHOTS_PER_PAGE=40"
      - "TIME_ZONE=UTC"
      - "USE_COLOR=False"
    labels:
      - "flame.type=application"
      - "flame.name=ArchiveBox"
      - "flame.url=https://wayback.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.routers.archivebox.rule=Host(`wayback.seedno.de`)"
      - "traefik.http.routers.archivebox.entrypoints=https"
      - "traefik.http.routers.archivebox.service=archivebox"
      - "traefik.http.routers.archivebox.tls=true"
      - "traefik.http.routers.archivebox.tls.certresolver=letsencrypt"
      - "traefik.http.routers.archivebox.middlewares=compress,errors,secure,whitelist@file"
      - "traefik.http.services.archivebox.loadbalancer.server.port=8000"
    networks:
      - traefik
      - archivebox
    volumes:
      - type: bind
        source: /docker/archivebox/data
        target: /data

  archivebox-sonic:
    image: valeriansaliou/sonic:v1.3.0
    container_name: archivebox-sonic
    restart: unless-stopped
    environment:
      - "SEARCH_BACKEND_PASSWORD=${ARCHIVEBOX_SEARCH_BACKEND_PASSWORD:?not set}"
    networks:
      - archivebox
    volumes:
      - type: bind
        source: /docker/archivebox/config/sonic.cfg
        target: /etc/sonic.cfg
        read_only: true
      - type: bind
        source: /docker/archivebox/sonic
        target: /var/lib/sonic/store

  ### https://wiki.archiveteam.org/index.php/ArchiveTeam_Warrior
  archiveteam:
    image: atdr.meo.ws/archiveteam/warrior-dockerfile
    container_name: archiveteam
    restart: unless-stopped
    environment:
      - "CONCURRENT_ITEMS=6"
      - "DOWNLOADER=Seednode"
      - "HTTP_USERNAME=${ARCHIVETEAM_USERNAME:?not set}"
      - "HTTP_PASSWORD=${ARCHIVETEAM_PASSWORD:?not set}"
      - "SELECTED_PROJECT=auto"
    labels:
      - "flame.type=application"
      - "flame.name=ArchiveTeam"
      - "flame.url=https://warrior.crimson.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.routers.archiveteam.rule=Host(`warrior.crimson.seedno.de`)"
      - "traefik.http.routers.archiveteam.entrypoints=https"
      - "traefik.http.routers.archiveteam.service=archiveteam"
      - "traefik.http.routers.archiveteam.tls=true"
      - "traefik.http.routers.archiveteam.tls.certresolver=letsencrypt"
      - "traefik.http.routers.archiveteam.middlewares=compress,errors,secure,whitelist@file"
      - "traefik.http.services.archiveteam.loadbalancer.server.port=8001"
    networks:
      - traefik

  ### https://github.com/BookStackApp/BookStack
  bookstack:
    image: ghcr.io/linuxserver/bookstack:latest
    container_name: bookstack
    restart: unless-stopped
    depends_on:
      - bookstack-db
    environment:
      - "PUID=${UID:?not set}"
      - "PGID=${GID:?not set}"
      - "APP_URL=https://bookstack.seedno.de"
      - "DB_HOST=bookstack-db"
      - "DB_USER=${BOOKSTACK_DATABASE_USER:?not set}"
      - "DB_PASS=${BOOKSTACK_DATABASE_PASS:?not set}"
      - "DB_DATABASE=${BOOKSTACK_DATABASE_NAME:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Bookstack"
      - "flame.url=https://bookstack.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.bookstack.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'"
      - "traefik.http.routers.bookstack.rule=Host(`bookstack.seedno.de`)"
      - "traefik.http.routers.bookstack.entrypoints=https"
      - "traefik.http.routers.bookstack.service=bookstack"
      - "traefik.http.routers.bookstack.tls=true"
      - "traefik.http.routers.bookstack.tls.certresolver=letsencrypt"
      - "traefik.http.routers.bookstack.middlewares=compress,errors,secure,bookstack,whitelist@file"
      - "traefik.http.services.bookstack.loadbalancer.server.port=80"
    networks:
      - traefik
      - bookstack
    volumes:
      - type: bind
        source: /docker/bookstack/config
        target: /config

  bookstack-db:
    image: ghcr.io/linuxserver/mariadb
    container_name: bookstack-db
    restart: unless-stopped
    environment:
      - "MYSQL_ROOT_PASSWORD=${BOOKSTACK_DATABASE_ROOT_PASS:?not set}"
      - "PUID=${UID:?not set}"
      - "PGID=${GID:?not set}"
      - "TZ=${TIMEZONE:?not set}"
      - "MYSQL_DATABASE=${BOOKSTACK_DATABASE_NAME:?not set}"
      - "MYSQL_USER=${BOOKSTACK_DATABASE_USER:?not set}"
      - "MYSQL_PASSWORD=${BOOKSTACK_DATABASE_PASS:?not set}"
    networks:
      - bookstack
    volumes:
      - type: bind
        source: /docker/bookstack/database
        target: /config

  ### https://github.com/cockroachdb/cockroach
  cockroach-db:
    image: cockroachdb/cockroach:latest-v22.1
    container_name: cockroach-db
    hostname: cockroach.crimson.seedno.de
    restart: unless-stopped
    command:
      - "start"
      - "--cache=.25"
      - "--max-sql-memory=.25"
      - "--join=cockroach.casa.seedno.de,cockroach.crimson.seedno.de,cockroach.storage.seedno.de"
      - "--advertise-addr=cockroach.crimson.seedno.de"
      - "--cluster-name=logging"
      - "--certs-dir=/certs"
    labels:
      - "flame.type=application"
      - "flame.name=CockroachDB"
      - "flame.url=https://cockroach.crimson.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.routers.cockroachdb.rule=Host(`cockroach.crimson.seedno.de`)"
      - "traefik.http.routers.cockroachdb.entrypoints=https"
      - "traefik.http.routers.cockroachdb.service=cockroachdb"
      - "traefik.http.routers.cockroachdb.tls=true"
      - "traefik.http.routers.cockroachdb.tls.certresolver=letsencrypt"
      - "traefik.http.routers.cockroachdb.middlewares=compress,errors,secure,whitelist@file"
      - "traefik.http.services.cockroachdb.loadbalancer.server.port=8080"
      - "traefik.http.services.cockroachdb.loadbalancer.server.scheme=https"
    ports:
      - "26257:26257"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/cockroachdb/certs
        target: /certs
        read_only: true
      - type: bind
        source: /docker/cockroachdb/data
        target: /cockroach/cockroach-data

  ### https://git.seedno.de/seednode/commands
  commands:
    image: oci.seedno.de/seednode/commands:latest
    container_name: commands
    restart: unless-stopped
    environment:
      - "TZ=${TIMEZONE:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Commands"
      - "flame.url=https://commands.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.commands.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'"
      - "traefik.http.routers.commands.rule=Host(`commands.seedno.de`)"
      - "traefik.http.routers.commands.entrypoints=https"
      - "traefik.http.routers.commands.service=commands"
      - "traefik.http.routers.commands.tls=true"
      - "traefik.http.routers.commands.tls.certresolver=letsencrypt"
      - "traefik.http.routers.commands.middlewares=adminauth,compress,errors,secure,commands,whitelist@file"
      - "traefik.http.services.commands.loadbalancer.server.port=8080"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/commands/config
        target: /home/nonroot/.config/commands

  ### https://github.com/gchq/CyberChef
  cyberchef:
    image: mpepping/cyberchef:latest
    container_name: cyberchef
    restart: unless-stopped
    labels:
      - "flame.type=application"
      - "flame.name=CyberChef"
      - "flame.url=https://tools.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.cyberchef.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'self' data:; frame-src 'self' data:; worker-src 'self' blob: data:"
      - "traefik.http.routers.cyberchef.rule=Host(`tools.seedno.de`)"
      - "traefik.http.routers.cyberchef.entrypoints=https"
      - "traefik.http.routers.cyberchef.service=cyberchef"
      - "traefik.http.routers.cyberchef.tls=true"
      - "traefik.http.routers.cyberchef.tls.certresolver=letsencrypt"
      - "traefik.http.routers.cyberchef.middlewares=compress,errors,secure,cyberchef"
      - "traefik.http.services.cyberchef.loadbalancer.server.port=8000"
    networks:
      - traefik

  ### https://github.com/jgraph/drawio
  drawio:
    image: jgraph/drawio:latest
    container_name: drawio
    restart: unless-stopped
    environment:
      - "KEYSTORE_PASS=${DRAWIO_KEYSTORE_PASS:?not set}"
      - "KEY_PASS=${DRAWIO_KEY_PASS:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Draw.io"
      - "flame.url=https://draw.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.drawio.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://www.draw.io/notifications"
      - "traefik.http.routers.drawio.rule=Host(`draw.seedno.de`)"
      - "traefik.http.routers.drawio.entrypoints=https"
      - "traefik.http.routers.drawio.service=drawio"
      - "traefik.http.routers.drawio.tls=true"
      - "traefik.http.routers.drawio.tls.certresolver=letsencrypt"
      - "traefik.http.routers.drawio.middlewares=compress,errors,secure,drawio"
      - "traefik.http.services.drawio.loadbalancer.server.port=8080"
    networks:
      - traefik

  ### https://github.com/elastic/elasticsearch
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.3.3
    container_name: elasticsearch
    restart: unless-stopped
    environment:
      - "discovery.type=single-node"
      - "ES_JAVA_OPTS=-Xms1G -Xmx4G"
      - "xpack.security.enabled=false"
    networks:
      - elasticsearch
    volumes:
      - type: bind
        source: /docker/elasticsearch/data
        target: /usr/share/elasticsearch/data

  ### https://github.com/skeeto/endlessh
  endlessh:
    image: ghcr.io/linuxserver/endlessh
    container_name: endlessh
    restart: unless-stopped
    environment:
      - "PUID=${UID:?not set}"
      - "PGID=${GID:?not set}"
      - "TZ=${TIMEZONE:?not set}"
      - "MSDELAY=10000"
      - "MAXLINES=32"
      - "MAXCLIENTS=4096"
      - "LOGFILE=true"
      - "BINDFAMILY=4"
    ports:
      - "22:2222"
    networks:
      - endlessh
    volumes:
      - type: bind
        source: /docker/endlessh/config
        target: /config

  ### https://git.seedno.de/seednode/docker-site-errors
  errors:
    image: oci.seedno.de/seednode/errors:latest
    container_name: errors
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.errors.entrypoints=http"
      - "traefik.http.services.errors.loadbalancer.server.port=8080"
    networks:
      - traefik

  ### https://github.com/pawelmalak/flame
  flame:
    image: pawelmalak/flame:latest
    container_name: flame
    restart: unless-stopped
    environment:
      - "PASSWORD=${FLAME_PASSWORD:?not set}"
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.flame.headers.contentSecurityPolicy=default-src 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self' https://raw.githubusercontent.com/"
      - "traefik.http.routers.flame.rule=Host(`flame.seedno.de`)"
      - "traefik.http.routers.flame.entrypoints=https"
      - "traefik.http.routers.flame.service=flame"
      - "traefik.http.routers.flame.tls=true"
      - "traefik.http.routers.flame.tls.certresolver=letsencrypt"
      - "traefik.http.routers.flame.middlewares=compress,errors,secure,flame,whitelist@file"
      - "traefik.http.services.flame.loadbalancer.server.port=5005"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/flame/data
        target: /app/data
      - type: bind
        source: /var/run/docker.sock
        target: /var/run/docker.sock
        read_only: true

  ### https://github.com/go-gitea/gitea
  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    restart: unless-stopped
    depends_on:
      - gitea-db
    environment:
      - "TMPDIR=/data/backups"
      - "USER_UID=${UID:?not set}"
      - "USER_GID=${GID:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Gitea"
      - "flame.url=https://git.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.gitea.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; font-src 'self' data:; img-src 'self' https: data:; manifest-src 'self' data:"
      - "traefik.http.routers.gitea.rule=Host(`git.seedno.de`)"
      - "traefik.http.routers.gitea.entrypoints=https"
      - "traefik.http.routers.gitea.service=gitea"
      - "traefik.http.routers.gitea.tls=true"
      - "traefik.http.routers.gitea.tls.certresolver=letsencrypt"
      - "traefik.http.routers.gitea.middlewares=compress,errors,secure,gitea"
      - "traefik.http.services.gitea.loadbalancer.server.port=3000"
    ports:
      - "9023:22"
    networks:
      - traefik
      - gitea
    volumes:
      - type: bind
        source: /docker/gitea/data
        target: /data
      - type: bind
        source: /etc/localtime
        target: /etc/localtime
        read_only: true
      - type: bind
        source: /docker/gitea/conf/sshd_config
        target: /etc/ssh/sshd_config
        read_only: true

  gitea-db:
    image: postgres:13-alpine
    container_name: gitea-db
    restart: unless-stopped
    environment:
      - "POSTGRES_DB=${GITEA_DATABASE_NAME:?not set}"
      - "POSTGRES_USER=${GITEA_DATABASE_USER:?not set}"
      - "POSTGRES_PASSWORD=${GITEA_DATABASE_PASS:?not set}"
    networks:
      - gitea
    volumes:
      - type: bind
        source: /docker/gitea/database
        target: /var/lib/postgresql/data

  ### https://github.com/grafana/grafana
  ## Set data source to "http://prometheus:9090"
  grafana-renderer:
    image: grafana/grafana-image-renderer:latest
    container_name: grafana-renderer
    restart: unless-stopped
    depends_on:
      - grafana
    environment:
      - "BROWSER_TZ=${TIMEZONE}"
      - "HTTP_HOST=0.0.0.0"
      - "HTTP_PORT=8081"
    networks:
      - grafana

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    depends_on:
      - prometheus
    environment:
      - "GF_ALERTING_ENABLED=false"
      - "GF_AUTH_ANONYMOUS_ENABLED=false"
      - "GF_DEFAULT_INSTANCE_NAME=grafana.crimson.seedno.de"
      - "GF_INSTALL_PLUGINS=grafana-piechart-panel,grafana-clock-panel"
      - "GF_LOG_FILTERS=rendering:debug"
      - "GF_METRICS_ENABLED=true"
      - "GF_PATHS_TEMP_DATA_LIFETIME=0"
      - "GF_RENDERING_SERVER_URL=http://grafana-renderer:8081/render"
      - "GF_RENDERING_CALLBACK_URL=http://grafana:3000/"
      - "GF_SECURITY_COOKIE_SECURE=true"
      - "GF_SECURITY_DISABLE_GRAVATAR=true"
      - "GF_SECURITY_ALLOW_EMBEDDING=false"
      - "GF_SERVER_DOMAIN=grafana.crimson.seedno.de"
      - "GF_SERVER_ENFORCE_DOMAIN=true"
      - "GF_SERVER_HTTP_PORT=3000"
      - "GF_SERVER_HTTP_PROTOCOL=https"
      - "GF_SERVER_ROOT_URL=https://grafana.crimson.seedno.de/"
      - "GF_SERVER_SERVE_FROM_SUB_PATH=false"
      - "GF_SNAPSHOTS_EXTERNAL_ENABLED=false"
      - "GF_USERS_ALLOW_SIGN_UP=false"
      - "GF_USERS_VIEWERS_CAN_EDIT=false"
    labels:
      - "flame.type=application"
      - "flame.name=Grafana"
      - "flame.url=https://grafana.crimson.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.grafana.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data:"
      - "traefik.http.routers.grafana.rule=Host(`grafana.crimson.seedno.de`)"
      - "traefik.http.routers.grafana.entrypoints=https"
      - "traefik.http.routers.grafana.service=grafana"
      - "traefik.http.routers.grafana.tls=true"
      - "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
      - "traefik.http.routers.grafana.middlewares=compress,errors,secure,grafana,whitelist@file"
      - "traefik.http.services.grafana.loadbalancer.server.port=3000"
    networks:
      - traefik
      - prometheus
      - grafana
    volumes:
      - type: bind
        source: /docker/grafana/data
        target: /var/lib/grafana

  ### https://github.com/apache/guacamole-server
  ## To generate the database schema:
  # $ docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --postgres > /tmp/initdb.sql
  # $ docker cp /tmp/initdb.sql guac-db:/tmp/initdb.sql
  # $ docker exec -it guac-db /bin/sh -c "cat /tmp/initdb.sql | psql --dbname=${GUACAMOLE_DATABASE_NAME} --user=${GUACAMOLE_DATABASE_USER} -f -"
  ## When configuring a connection, set the following for guacd parameters:
  # - Hostname: guacd-internal
  # - Port: 4822
  # - Encryption: None (unencrypted)
  ## RDP sessions should have a security mode of "NLA (Network Level Authentication)"
  guacamole:
    image: guacamole/guacamole:latest
    container_name: guacamole
    restart: unless-stopped
    environment:
      - "GUACD_HOSTNAME=guacd-internal"
      - "POSTGRES_HOSTNAME=guacamole-db"
      - "POSTGRES_DATABASE=${GUACAMOLE_DATABASE_NAME:?not set}"
      - "POSTGRES_USER=${GUACAMOLE_DATABASE_USER:?not set}"
      - "POSTGRES_PASSWORD=${GUACAMOLE_DATABASE_PASS:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Guacamole"
      - "flame.url=https://guacamole.seedno.de/guacamole/"
      - "traefik.enable=true"
      - "traefik.http.middlewares.guacamole.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; img-src 'self' data:"
      - "traefik.http.routers.guacamole.rule=Host(`guacamole.seedno.de`) && PathPrefix(`/guacamole/`)"
      - "traefik.http.routers.guacamole.entrypoints=https"
      - "traefik.http.routers.guacamole.service=guacamole"
      - "traefik.http.routers.guacamole.tls=true"
      - "traefik.http.routers.guacamole.tls.certresolver=letsencrypt"
      - "traefik.http.routers.guacamole.middlewares=compress,errors,secure,guacamole,whitelist@file"
      - "traefik.http.services.guacamole.loadbalancer.server.port=8080"
    networks:
      - traefik
      - guacamole

  guacamole-db:
    image: postgres:13-alpine
    container_name: guacamole-db
    restart: unless-stopped
    environment:
      - "POSTGRES_DB=${GUACAMOLE_DATABASE_NAME:?not set}"
      - "POSTGRES_USER=${GUACAMOLE_DATABASE_USER:?not set}"
      - "POSTGRES_PASSWORD=${GUACAMOLE_DATABASE_PASS:?not set}"
    networks:
      - guacamole
    volumes:
      - type: bind
        source: /docker/guacamole/database
        target: /var/lib/postgresql/data

  guacd:
    image: linuxserver/guacd:latest
    container_name: guacd
    restart: unless-stopped
    networks:
      traefik:
      guacamole:
        aliases:
          - guacd-internal

  ### https://github.com/toptal/haste-server
  hastebin:
    image: angristan/hastebin:latest
    container_name: hastebin
    restart: unless-stopped
    environment:
      - "UID=${UID:?not set}"
      - "GID=${GID:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Hastebin"
      - "flame.url=https://haste.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.hastebin.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' ajax.googleapis.com"
      - "traefik.http.routers.hastebin.rule=Host(`haste.seedno.de`)"
      - "traefik.http.routers.hastebin.entrypoints=https"
      - "traefik.http.routers.hastebin.service=hastebin"
      - "traefik.http.routers.hastebin.tls=true"
      - "traefik.http.routers.hastebin.tls.certresolver=letsencrypt"
      - "traefik.http.routers.hastebin.middlewares=compress,errors,secure,hastebin"
      - "traefik.http.services.hastebin.loadbalancer.server.port=7777"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/hastebin/data
        target: /app/data

  ### https://github.com/zelon88/HRConvert2
  hrconvert2:
    image: dwaaan/hrconvert2-docker:latest
    container_name: hrconvert2
    restart: unless-stopped
    labels:
      - "flame.type=application"
      - "flame.name=HRConvert2"
      - "flame.url=https://convert.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.hrconvert2.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; img-src 'self' data:"
      - "traefik.http.routers.hrconvert2.rule=Host(`convert.seedno.de`)"
      - "traefik.http.routers.hrconvert2.entrypoints=https"
      - "traefik.http.routers.hrconvert2.service=hrconvert2"
      - "traefik.http.routers.hrconvert2.tls=true"
      - "traefik.http.routers.hrconvert2.tls.certresolver=letsencrypt"
      - "traefik.http.routers.hrconvert2.middlewares=adminauth,compress,errors,secure,hrconvert2,whitelist@file"
      - "traefik.http.services.hrconvert2.loadbalancer.server.port=80"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/hrconvert2/data/config.php
        target: /var/www/html/HRProprietary/HRConvert2/config.php
        read_only: true
      - type: bind
        source: /docker/hrconvert2/logs
        target: /var/www/html/HRProprietary/HRConvert2/Logs

  ### https://github.com/imgproxy/imgproxy
  imgproxy:
    image: darthsim/imgproxy:latest
    container_name: imgproxy
    restart: unless-stopped
    environment:
      - "IMGPROXY_KEY=${IMGPROXY_KEY:?not set}"
      - "IMGPROXY_SALT=${IMGPROXY_SALT:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Imgproxy"
      - "flame.url=https://imgproxy.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.imgproxy.headers.contentSecurityPolicy=default-src 'self'"
      - "traefik.http.routers.imgproxy.rule=Host(`imgproxy.seedno.de`)"
      - "traefik.http.routers.imgproxy.entrypoints=https"
      - "traefik.http.routers.imgproxy.service=imgproxy"
      - "traefik.http.routers.imgproxy.tls=true"
      - "traefik.http.routers.imgproxy.tls.certresolver=letsencrypt"
      - "traefik.http.routers.imgproxy.middlewares=compress,errors,secure,imgproxy"
      - "traefik.http.services.imgproxy.loadbalancer.server.port=8080"
    networks:
      - traefik

  ### https://github.com/jellyfin/jellyfin
  jellyfin:
    image: ghcr.io/linuxserver/jellyfin:latest
    container_name: jellyfin
    restart: unless-stopped
    environment:
      - "PUID=${UID:?not set}"
      - "PGID=${GID:?not set}"
      - "TZ=${TIMEZONE:?not set}"
      - "JELLYFIN_PublishedServerUrl=watch.seedno.de"
    labels:
      - "flame.type=application"
      - "flame.name=Jellyfin"
      - "flame.url=https://jellyfin.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.routers.jellyfin.rule=Host(`watch.seedno.de`)"
      - "traefik.http.routers.jellyfin.entrypoints=https"
      - "traefik.http.routers.jellyfin.service=jellyfin"
      - "traefik.http.routers.jellyfin.tls=true"
      - "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
      - "traefik.http.routers.jellyfin.middlewares=compress,errors,secure,whitelist@file"
      - "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
    devices:
      - /dev/dri/renderD128:/dev/dri/renderD128
      - /dev/dri/card0:/dev/dri/card0
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/jellyfin/config
        target: /config
      - type: bind
        source: /storage
        target: /data
        read_only: true

  ### https://github.com/mastodon/mastodon
  mastodon-db:
    image: postgres:14-alpine
    container_name: mastodon-db
    restart: unless-stopped
    shm_size: 256mb
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
    environment:
      - "POSTGRES_DB=${MASTODON_DATABASE_NAME:?not set}"
      - "POSTGRES_PASSWORD=${MASTODON_DATABASE_PASS:?not set}"
    networks:
      - mastodon
    volumes:
      - type: bind
        source: /docker/mastodon/database
        target: /var/lib/postgresql/data

  mastodon-es:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.17.4
    container_name: mastodon-es
    restart: unless-stopped
    environment:
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m -Des.enforce.bootstrap.checks=true"
      - "xpack.license.self_generated.type=basic"
      - "xpack.security.enabled=false"
      - "xpack.watcher.enabled=false"
      - "xpack.graph.enabled=false"
      - "xpack.ml.enabled=false"
      - "bootstrap.memory_lock=true"
      - "cluster.name=es-mastodon"
      - "discovery.type=single-node"
      - "thread_pool.write.queue_size=1000"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    networks:
       - mastodon
    volumes:
       - type: bind
         source: /docker/mastodon/elasticsearch
         target: /usr/share/elasticsearch/data

  mastodon-redis:
    image: redis:6-alpine
    container_name: mastodon-redis
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
    networks:
      - mastodon
    volumes:
      - type: bind
        source: /docker/mastodon/redis
        target: /data

  mastodon-sidekiq:
    image: tootsuite/mastodon
    container_name: mastodon-sidekiq
    restart: always
    command: bundle exec sidekiq
    env_file: .env.mastodon
    depends_on:
      - mastodon-db
      - mastodon-redis
    networks:
      - traefik
      - mastodon
    volumes:
      - type: bind
        source: /docker/mastodon/public/system
        target: /mastodon/public/system
      - type: bind
        source: /docker/mastodon/favicon/favicon.ico
        target: /mastodon/public/favicon.ico
        read_only: true

  mastodon-streaming:
    image: tootsuite/mastodon
    container_name: mastodon-streaming
    restart: unless-stopped
    command: node ./streaming
    env_file: .env.mastodon
    depends_on:
      - mastodon-db
      - mastodon-redis
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1"]
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.mastodon-streaming.rule=Host(`placidity.social`) && PathPrefix(`/api/v1/streaming`)"
      - "traefik.http.routers.mastodon-streaming.entrypoints=https"
      - "traefik.http.routers.mastodon-streaming.service=mastodon-streaming"
      - "traefik.http.routers.mastodon-streaming.tls=true"
      - "traefik.http.routers.mastodon-streaming.tls.certresolver=letsencrypt"
      - "traefik.http.routers.mastodon-streaming.middlewares=compress,errors,secure"
      - "traefik.http.services.mastodon-streaming.loadbalancer.server.port=4000"
    networks:
      - traefik
      - mastodon

  mastodon-web:
    image: tootsuite/mastodon
    container_name: mastodon-web
    restart: unless-stopped
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
    env_file: .env.mastodon
    depends_on:
      - mastodon-db
      - mastodon-redis
    healthcheck:
      test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:3000/health || exit 1']
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.mastodon-web.rule=Host(`placidity.social`)"
      - "traefik.http.routers.mastodon-web.entrypoints=https"
      - "traefik.http.routers.mastodon-web.service=mastodon-web"
      - "traefik.http.routers.mastodon-web.tls=true"
      - "traefik.http.routers.mastodon-web.tls.certresolver=letsencrypt"
      - "traefik.http.routers.mastodon-web.middlewares=compress,errors,secure"
      - "traefik.http.services.mastodon-web.loadbalancer.server.port=3000"
    networks:
      - traefik
      - mastodon
    volumes:
      - type: bind
        source: /docker/mastodon/public/system
        target: /mastodon/public/system
      - type: bind
        source: /docker/mastodon/favicon/favicon.ico
        target: /mastodon/public/favicon.ico
        read_only: true

  ## https://github.com/danmanners/memegen
  memegen:
    image: oci.seedno.de/seednode/memegen:latest
    container_name: memegen
    restart: unless-stopped
    labels:
      - "flame.type=application"
      - "flame.name=MemeGen"
      - "flame.url=https://memegen.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.memegen.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; img-src 'self' data: https://validator.swagger.io"
      - "traefik.http.routers.memegen.rule=Host(`memegen.seedno.de`)"
      - "traefik.http.routers.memegen.entrypoints=https"
      - "traefik.http.routers.memegen.service=memegen"
      - "traefik.http.routers.memegen.tls=true"
      - "traefik.http.routers.memegen.tls.certresolver=letsencrypt"
      - "traefik.http.routers.memegen.middlewares=compress,errors,secure,memegen"
      - "traefik.http.services.memegen.loadbalancer.server.port=5000"
    networks:
      - traefik

  ### https://github.com/itzg/docker-minecraft-server
  ## To automatically install mods, create a file called mods.txt
  ## and bind mount it, setting MODS_FILE accordingly, with one
  ## URL per line. For example, to install WorldEdit and WorldGuard:
  ## https://dev.bukkit.org/projects/worldedit/files/latest
  ## https://dev.bukkit.org/projects/worldguard/files/latest
  minecraft1:
    image: itzg/minecraft-server:latest
    container_name: minecraft1
    restart: unless-stopped
    environment:
      - "AUTOPAUSE_PERIOD=10"
      - "AUTOPAUSE_TIMEOUT_EST=300"
      - "AUTOPAUSE_TIMEOUT_INIT=300"
      - "AUTOPAUSE_TIMEOUT_KN=60"
      - "ENABLE_AUTOPAUSE=true"
      - "ENABLE_COMMAND_BLOCK=false"
      - "ENABLE_ROLLING_LOGS=true"
      - "EULA=TRUE"
      - "FORCE_GAMEMODE=true"
      - "ICON=https://cdn.seedno.de/img/seednode.jpg"
      - "JVM_DD_OPTS=disable.watchdog:true"
      - "MAX_PLAYERS=16"
      - "MAX_TICK_TIME=-1"
      - "MEMORY=4G"
      - "MODE=survival"
      - "MODS_FILE=/mods.txt"
      - "MOTD=§cWelcome to Hell!§r"
      - "ONLINE_MODE=true"
      - "OVERRIDE_SERVER_PROPERTIES=true"
      - "PLAYER_IDLE_TIMEOUT=0"
      - "PREVENT_PROXY_CONNECTIONS=true"
      - "REMOVE_OLD_MODS=true"
      - "SERVER_PORT=25565"
      - "SNOOPER_ENABLED=false"
      - "TYPE=SPIGOT"
      - "USE_AIKAR_FLAGS=true"
      - "VERSION=LATEST"
      - "VIEW_DISTANCE=12"
      - "UID=${UID:?not set}"
      - "GID=${GID:?not set}"
      - "TZ=${TIMEZONE:?not set}"
    ports:
      - "25565:25565"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/minecraft/server1
        target: /data
      - type: bind
        source: /docker/minecraft/config/mods.txt
        target: /mods.txt
        read_only: true

  ### https://github.com/minio/minio
  ## Set up minio-client alias
  # $ mc alias set minio https://minio.seedno.de  
  ## Generate JWT bearer token
  # $ mc admin prometheus generate minio
  minio:
    image: minio/minio:latest
    container_name: minio
    restart: unless-stopped
    command:
      - "server"
      - "/data"
      - "--console-address"
      - ":9001"
    environment:
      - "MINIO_BROWSER_REDIRECT_URL=https://s3.seedno.de"
      - "MINIO_PROMETHEUS_AUTH_TYPE=jwt"
      - "MINIO_PROMETHEUS_JOB_ID=minio-job"
      - "MINIO_PROMETHEUS_URL=http://prometheus:9090"
      - "MINIO_ROOT_USER=${MINIO_ROOT_USER:?not set}"
      - "MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASS:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Minio"
      - "flame.url=https://console.minio.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.routers.minio-server.rule=Host(`minio.crimson.seedno.de`)"
      - "traefik.http.routers.minio-server.entrypoints=https"
      - "traefik.http.routers.minio-server.service=minio-server"
      - "traefik.http.routers.minio-server.tls=true"
      - "traefik.http.routers.minio-server.tls.certresolver=letsencrypt"
      - "traefik.http.routers.minio-server.middlewares=compress,errors,secure,whitelist@file"
      - "traefik.http.services.minio-server.loadbalancer.server.port=9000"
      - "traefik.http.routers.minio-console.rule=Host(`s3.seedno.de`)"
      - "traefik.http.routers.minio-console.entrypoints=https"
      - "traefik.http.routers.minio-console.service=minio-console"
      - "traefik.http.routers.minio-console.tls=true"
      - "traefik.http.routers.minio-console.tls.certresolver=letsencrypt"
      - "traefik.http.routers.minio-console.middlewares=compress,errors,secure,whitelist@file"
      - "traefik.http.services.minio-console.loadbalancer.server.port=9001"
    networks:
      - traefik
      - prometheus
    volumes:
      - type: bind
        source: /docker/minio/data
        target: /data

  ### https://github.com/codetheweb/muse
  muse:
    image: codetheweb/muse:latest
    container_name: muse
    restart: unless-stopped
    environment:
      - "DISCORD_TOKEN=${MUSE_DISCORD_TOKEN}"
      - "YOUTUBE_API_KEY=${MUSE_YOUTUBE_API_KEY}"
      - "SPOTIFY_CLIENT_ID=${MUSE_SPOTIFY_CLIENT_ID}"
      - "SPOTIFY_CLIENT_SECRET=${MUSE_SPOTIFY_CLIENT_SECRET}"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/muse/data
        target: /data

  ### https://git.seedno.de/seednode/docker-site-naughty
  naughty:
    image: oci.seedno.de/seednode/naughty:latest
    container_name: naughty
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.naughty.rule=PathPrefix(`/{path:.*(\\.env|\\.aws|xmlrpc|wp-admin|wp-login).*}`)"
      - "traefik.http.routers.naughty.priority=255"
      - "traefik.http.routers.naughty.entrypoints=https"
      - "traefik.http.routers.naughty.service=naughty"
      - "traefik.http.routers.naughty.tls=true"
      - "traefik.http.routers.naughty.tls.certresolver=letsencrypt"
      - "traefik.http.routers.naughty.middlewares=compress,errors,secure"
      - "traefik.http.services.naughty.loadbalancer.server.port=8080"
    networks:
      - traefik

  ### https://github.com/m1k1o/neko
  neko:
    image: m1k1o/neko:firefox
    container_name: neko
    restart: unless-stopped
    shm_size: 4gb
    environment:
      - "NEKO_AUDIO_BITRATE=192"
      - "NEKO_CONTROL_PROTECTION=true"
      - "NEKO_UDPMUX=52100"
      - "NEKO_IMPLICIT_CONTROL=true"
      - "NEKO_LOCKS=controls"
      - "NEKO_MAX_FPS=60"
      - "NEKO_OPUS=true"
      - "NEKO_PASSWORD=${NEKO_PASS:?not set}"
      - "NEKO_PASSWORD_ADMIN=${NEKO_ADMIN_PASS:?not set}"
      - "NEKO_SCREEN=1280x720@30"
      - "NEKO_VIDEO_BITRATE=3500"
      - "NEKO_VP8=true"
    labels:
      - "flame.type=application"
      - "flame.name=Neko"
      - "flame.url=https://neko.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.neko.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; connect-src 'self' wss:"
      - "traefik.http.routers.neko.rule=Host(`neko.seedno.de`)"
      - "traefik.http.routers.neko.entrypoints=https"
      - "traefik.http.routers.neko.service=neko"
      - "traefik.http.routers.neko.tls=true"
      - "traefik.http.routers.neko.tls.certresolver=letsencrypt"
      - "traefik.http.routers.neko.middlewares=compress,errors,secure,neko"
      - "traefik.http.services.neko.loadbalancer.server.port=8080"
    ports:
      - "52100:52100/udp"
    networks:
      - traefik

  ### https://github.com/netdata/netdata
  netdata:
    image: netdata/netdata:latest
    container_name: netdata
    hostname: netdata.crimson.seedno.de
    restart: unless-stopped
    cap_add:
      - "SYS_PTRACE"
    security_opt:
      - "apparmor:unconfined"
    labels:
      - "flame.type=application"
      - "flame.name=Netdata"
      - "flame.url=https://netdata.crimson.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.netdata.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; img-src 'self' data:; connect-src 'self' https://registry.my-netdata.io https://www.googleapis.com; frame-src 'self' https://app.netdata.cloud"
      - "traefik.http.routers.netdata.rule=Host(`netdata.crimson.seedno.de`)"
      - "traefik.http.routers.netdata.entrypoints=https"
      - "traefik.http.routers.netdata.service=netdata"
      - "traefik.http.routers.netdata.tls=true"
      - "traefik.http.routers.netdata.tls.certresolver=letsencrypt"
      - "traefik.http.routers.netdata.middlewares=compress,errors,secure,netdata,whitelist@file"
      - "traefik.http.services.netdata.loadbalancer.server.port=19999"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /proc
        target: /host/proc
        read_only: true
      - type: bind
        source: /sys
        target: /host/sys
        read_only: true

  ### https://git.seedno.de/seednode/docker-nginx
  ## All my sites are configured via the file provider
  ## An example is available here:
  ## https://cdn.seedno.de/txt/access.seedno.de.yaml
  nginx:
    image: oci.seedno.de/seednode/nginx:latest
    container_name: nginx
    restart: unless-stopped
    depends_on:
      - nginx-php-fpm
    networks:
      - traefik
      - php
    volumes:
      - type: bind
        source: /docker/nginx/config
        target: /etc/nginx
        read_only: true
      - type: bind
        source: /docker/nginx/logs
        target: /var/log/nginx
      - type: bind
        source: /storage
        target: /storage
        read_only: true
      - type: bind
        source: /var/www/html
        target: /var/www/html
        read_only: true

  nginx-php-fpm:
    image: php:8-fpm-alpine
    container_name: nginx-php-fpm
    restart: unless-stopped
    networks:
      - php
    volumes:
      - type: bind
        source: /storage
        target: /storage
        read_only: true
      - type: bind
        source: /var/www/html
        target: /var/www/html
        read_only: true

  ### https://github.com/Luzifer/ots
  ots:
    image: oci.seedno.de/seednode/ots:latest
    container_name: ots
    restart: unless-stopped
    labels:
      - "flame.type=application"
      - "flame.name=OTS"
      - "flame.url=https://ots.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.ots.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; script-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' https://fonts.gstatic.com"
      - "traefik.http.routers.ots.rule=Host(`secrets.seedno.de`)"
      - "traefik.http.routers.ots.entrypoints=https"
      - "traefik.http.routers.ots.service=ots"
      - "traefik.http.routers.ots.tls=true"
      - "traefik.http.routers.ots.tls.certresolver=letsencrypt"
      - "traefik.http.routers.ots.middlewares=compress,errors,secure,ots"
      - "traefik.http.services.ots.loadbalancer.server.port=3000"
    networks:
      - traefik

  ### https://git.seedno.de/seednode/docker-oui
  oui:
    image: oci.seedno.de/seednode/oui-web:latest
    container_name: oui
    restart: unless-stopped
    labels:
      - "flame.type=application"
      - "flame.name=OUI Web Lookup"
      - "flame.url=https://oui.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.oui.headers.contentSecurityPolicy=default-src 'self'"
      - "traefik.http.routers.oui.rule=Host(`oui.seedno.de`)"
      - "traefik.http.routers.oui.entrypoints=https"
      - "traefik.http.routers.oui.service=oui"
      - "traefik.http.routers.oui.tls=true"
      - "traefik.http.routers.oui.tls.certresolver=letsencrypt"
      - "traefik.http.routers.oui.middlewares=compress,errors,secure,ratelimit,oui"
      - "traefik.http.services.oui.loadbalancer.server.port=8080"
    networks:
      - traefik

  ### https://github.com/owncast/owncast
  owncast:
    image: gabekangas/owncast:latest
    container_name: owncast
    restart: unless-stopped
    labels:
      - "flame.type=application"
      - "flame.name=Owncast"
      - "flame.url=https://stream.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.owncast.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' blob:; img-src 'self' data:; font-src 'self' data:; media-src 'self' blob:"
      - "traefik.http.routers.owncast.rule=Host(`stream.seedno.de`)"
      - "traefik.http.routers.owncast.entrypoints=https"
      - "traefik.http.routers.owncast.service=owncast"
      - "traefik.http.routers.owncast.tls=true"
      - "traefik.http.routers.owncast.tls.certresolver=letsencrypt"
      - "traefik.http.routers.owncast.middlewares=compress,errors,secure,owncast"
      - "traefik.http.services.owncast.loadbalancer.server.port=8080"
    ports:
      - "1935:1935"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/owncast/data
        target: /app/data

  ### https://github.com/jonaswinkler/paperless-ng
  paperless-ng:
    image: ghcr.io/linuxserver/paperless-ng:latest
    container_name: paperless-ng
    restart: unless-stopped
    depends_on:
      - paperless-ng-redis
    environment:
      - "PUID=${UID:?not set}"
      - "PGID=${GID:?not set}"
      - "TZ=${TIMEZONE:?not set}"
      - "REDIS_URL=paperless-ng-redis:6379"
    labels:
      - "flame.type=application"
      - "flame.name=Paperless-ng"
      - "flame.url=https://paperless.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.paperless-ng.headers.contentSecurityPolicy=default-src 'self'; img-src 'self' data:"
      - "traefik.http.routers.paperless-ng.rule=Host(`paperless.seedno.de`)"
      - "traefik.http.routers.paperless-ng.entrypoints=https"
      - "traefik.http.routers.paperless-ng.service=paperless-ng"
      - "traefik.http.routers.paperless-ng.tls=true"
      - "traefik.http.routers.paperless-ng.tls.certresolver=letsencrypt"
      - "traefik.http.routers.paperless-ng.middlewares=compress,errors,secure,paperless-ng,whitelist@file"
      - "traefik.http.services.paperless-ng.loadbalancer.server.port=8000"
    networks:
      - traefik
      - paperless-ng
    volumes:
      - type: bind
        source: /docker/paperless-ng/config
        target: /config
      - type: bind
        source: /docker/paperless-ng/data
        target: /data

  paperless-ng-redis:
    image: redis
    container_name: paperless-ng-redis
    restart: unless-stopped
    networks:
      - paperless-ng

  ### https://github.com/mkaczanowski/pastebin
  pastebin:
    image: mkaczanowski/pastebin:latest
    container_name: pastebin
    restart: unless-stopped
    command:
      - "--address=0.0.0.0"
      - "--port=8000"
      - "--db=/data/pastebin.db"
      - "--uri=https://paste.seedno.de"
      - "--slug-len=12"
    labels:
      - "flame.type=application"
      - "flame.name=Pastebin"
      - "flame.url=https://paste.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.pastebin.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' https://cdnjs.cloudflare.com; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data:"
      - "traefik.http.routers.pastebin.rule=Host(`paste.seedno.de`)"
      - "traefik.http.routers.pastebin.entrypoints=https"
      - "traefik.http.routers.pastebin.service=pastebin"
      - "traefik.http.routers.pastebin.tls=true"
      - "traefik.http.routers.pastebin.tls.certresolver=letsencrypt"
      - "traefik.http.routers.pastebin.middlewares=compress,errors,secure,pastebin"
      - "traefik.http.services.pastebin.loadbalancer.server.port=8000"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/pastebin/data
        target: /data

  ### https://github.com/akhilrex/podgrab
  podgrab:
    image: akhilrex/podgrab:latest
    container_name: podgrab
    restart: unless-stopped
    environment:
      - "CHECK_FREQUENCY=60"
    labels:
      - "flame.type=application"
      - "flame.name=Podgrab"
      - "flame.url=https://podcasts.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.podgrab.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'"
      - "traefik.http.routers.podgrab.rule=Host(`podcasts.seedno.de`)"
      - "traefik.http.routers.podgrab.entrypoints=https"
      - "traefik.http.routers.podgrab.service=podgrab"
      - "traefik.http.routers.podgrab.tls=true"
      - "traefik.http.routers.podgrab.tls.certresolver=letsencrypt"
      - "traefik.http.routers.podgrab.middlewares=compress,errors,secure,podgrab,whitelist@file"
      - "traefik.http.services.podgrab.loadbalancer.server.port=8080"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/podgrab/config
        target: /config
      - type: bind
        source: /docker/podgrab/data
        target: /assets

  ### https://github.com/agersant/polaris
  polaris:
    image: ogarcia/polaris:latest
    container_name: polaris
    restart: unless-stopped
    labels:
      - "flame.type=application"
      - "flame.name=Polaris"
      - "flame.url=https://music.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.polaris.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; img-src 'self' data: https://*.swagger.io"
      - "traefik.http.routers.polaris.rule=Host(`music.seedno.de`)"
      - "traefik.http.routers.polaris.entrypoints=https"
      - "traefik.http.routers.polaris.service=polaris"
      - "traefik.http.routers.polaris.tls=true"
      - "traefik.http.routers.polaris.tls.certresolver=letsencrypt"
      - "traefik.http.routers.polaris.middlewares=compress,errors,secure,polaris,whitelist@file"
      - "traefik.http.services.polaris.loadbalancer.server.port=5050"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/polaris/data
        target: /var/lib/polaris
      - type: bind
        source: /storage/audio
        target: /music
        read_only: true

  ## https://github.com/prometheus/prometheus
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
      - "--storage.tsdb.path=/prometheus"
      - "--storage.tsdb.retention.time=30d"
      - "--web.console.libraries=/usr/share/prometheus/console_libraries"
      - "--web.console.templates=/usr/share/prometheus/consoles"
    networks:
      - prometheus
    volumes:
      - type: bind
        source: /docker/prometheus/config/prometheus.yml
        target: /etc/prometheus/prometheus.yml
        read_only: true
      - type: bind
        source: /docker/prometheus/data
        target: /prometheus

  ### https://github.com/kgretzky/pwndrop
  pwndrop:
    image: ghcr.io/linuxserver/pwndrop:latest
    container_name: pwndrop
    restart: unless-stopped
    environment:
      - "PUID=${UID:?not set}"
      - "PGID=${GID:?not set}"
      - "TZ=${TIMEZONE:?not set}"
      - "SECRET_PATH=${PWNDROP_SECRET_PATH:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Pwndrop"
      - "flame.url=https://files.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.pwndrop.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' https://fonts.googleapis.com; script-src 'self' 'unsafe-inline' 'unsafe-eval'; font-src 'self' data: https://fonts.gstatic.com/"
      - "traefik.http.routers.pwndrop-admin.rule=Host(`files.seedno.de`) && PathPrefix(`${PWNDROP_SECRET_PATH}`)"
      - "traefik.http.routers.pwndrop-admin.entrypoints=https"
      - "traefik.http.routers.pwndrop-admin.service=pwndrop"
      - "traefik.http.routers.pwndrop-admin.tls=true"
      - "traefik.http.routers.pwndrop-admin.tls.certresolver=letsencrypt"
      - "traefik.http.routers.pwndrop-admin.middlewares=compress,errors,secure,pwndrop"
      - "traefik.http.routers.pwndrop.rule=Host(`files.seedno.de`)"
      - "traefik.http.routers.pwndrop.entrypoints=https"
      - "traefik.http.routers.pwndrop.service=pwndrop"
      - "traefik.http.routers.pwndrop.tls=true"
      - "traefik.http.routers.pwndrop.tls.certresolver=letsencrypt"
      - "traefik.http.routers.pwndrop.middlewares=compress,errors,secure,pwndrop"
      - "traefik.http.services.pwndrop.loadbalancer.server.port=8080"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/pwndrop/config
        target: /config

  ### https://github.com/qbittorrent/qBittorrent
  qbittorrent:
    image: ghcr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    restart: unless-stopped
    environment:
      - "PUID=${UID:?not set}"
      - "PGID=${GID:?not set}"
      - "TZ=${TIMEZONE:?not set}"
      - "UMASK_SET=022"
      - "WEBUI_PORT=8080"
    labels:
      - "flame.type=application"
      - "flame.name=QBittorrent"
      - "flame.url=https://torrent.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.qbittorrent.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'"
      - "traefik.http.routers.qbittorrent.rule=Host(`torrent.seedno.de`)"
      - "traefik.http.routers.qbittorrent.entrypoints=https"
      - "traefik.http.routers.qbittorrent.service=qbittorrent"
      - "traefik.http.routers.qbittorrent.tls=true"
      - "traefik.http.routers.qbittorrent.tls.certresolver=letsencrypt"
      - "traefik.http.routers.qbittorrent.middlewares=compress,errors,secure,qbittorrent,whitelist@file"
      - "traefik.http.services.qbittorrent.loadbalancer.server.port=8080"
    ports:
      - "6881:6881"
      - "6881:6881/udp"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/qbittorrent/config
        target: /config
      - type: bind
        source: /storage/media/torrents
        target: /downloads

  ### https://git.seedno.de/seednode/docker-roulette
  roulette:
    image: oci.seedno.de/seednode/roulette:latest
    container_name: roulette
    restart: unless-stopped
    environment:
      - "TZ=${TIMEZONE:?not set}"
    command:
      - "--port=8080"
      - "--recursive"
      - "--verbose"
      - "/data/Images"
      - "/data/GIFs"
    labels:
      - "flame.type=application"
      - "flame.name=Random image roulette"
      - "flame.url=https://roulette.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.roulette.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'"
      - "traefik.http.routers.roulette.rule=Host(`roulette.seedno.de`)"
      - "traefik.http.routers.roulette.entrypoints=https"
      - "traefik.http.routers.roulette.service=roulette"
      - "traefik.http.routers.roulette.tls=true"
      - "traefik.http.routers.roulette.tls.certresolver=letsencrypt"
      - "traefik.http.routers.roulette.middlewares=adminauth,compress,secure,roulette"
      - "traefik.http.services.roulette.loadbalancer.server.port=8080"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /media/private
        target: /data
        read_only: true

  ### https://hub.docker.com/_/registry
  registry:
    image: registry:2
    container_name: registry
    restart: unless-stopped
    environment:
      - "REGISTRY_HTTP_ADDR=0.0.0.0:5000"
      - "REGISTRY_HTTP_SECRET=${REGISTRY_SECRET:-not set}"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.registry.rule=Host(`docker.seedno.de`)"
      - "traefik.http.routers.registry.entrypoints=https"
      - "traefik.http.routers.registry.service=registry"
      - "traefik.http.routers.registry.tls=true"
      - "traefik.http.routers.registry.tls.certresolver=letsencrypt"
      - "traefik.http.routers.registry.middlewares=compress,errors,secure,whitelist@file"
      - "traefik.http.services.registry.loadbalancer.server.port=5000"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/registry/config/config_private.yml
        target: /etc/docker/registry/config.yml
        read_only: true
      - type: bind
        source: /docker/registry/data
        target: /var/lib/registry

  ### Some people follow the actual guide for running a public registry,
  ### and some people just run a second copy of the service in maintenance
  ### mode and then disable write access to the volume
  registry-public:
    image: registry:2
    container_name: registry-public
    restart: unless-stopped
    environment:
      - "REGISTRY_HTTP_ADDR=0.0.0.0:5000"
      - "REGISTRY_HTTP_SECRET=${REGISTRY_SECRET:-not set}"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.registry-protected.rule=Host(`oci.seedno.de`) && PathPrefix(`/v2/_catalog`)"
      - "traefik.http.routers.registry-protected.entrypoints=https"
      - "traefik.http.routers.registry-protected.service=registry"
      - "traefik.http.routers.registry-protected.tls=true"
      - "traefik.http.routers.registry-protected.tls.certresolver=letsencrypt"
      - "traefik.http.routers.registry-protected.middlewares=compress,errors,secure,whitelist@file"
      - "traefik.http.services.registry-protected.loadbalancer.server.port=5000"
      - "traefik.http.routers.registry-public.rule=Host(`oci.seedno.de`)"
      - "traefik.http.routers.registry-public.entrypoints=https"
      - "traefik.http.routers.registry-public.service=registry-public"
      - "traefik.http.routers.registry-public.tls=true"
      - "traefik.http.routers.registry-public.tls.certresolver=letsencrypt"
      - "traefik.http.routers.registry-public.middlewares=compress,errors,secure"
      - "traefik.http.services.registry-public.loadbalancer.server.port=5000"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/registry/config/config_public.yml
        target: /etc/docker/registry/config.yml
        read_only: true
      - type: bind
        source: /docker/registry/data
        target: /var/lib/registry
        read_only: true

  ### https://github.com/ekzhang/rustpad
  rustpad:
    image: ekzhang/rustpad:latest
    container_name: rustpad
    restart: unless-stopped
    environment:
      - "EXPIRY_DAYS=1"
      - "SQLITE_URI=file:/app/db.sqlite"
    labels:
      - "flame.type=application"
      - "flame.name=Rustpad"
      - "flame.url=https://rustpad.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.rustpad.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net; font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net; worker-src 'self' blob:"
      - "traefik.http.routers.rustpad.rule=Host(`rustpad.seedno.de`)"
      - "traefik.http.routers.rustpad.entrypoints=https"
      - "traefik.http.routers.rustpad.service=rustpad"
      - "traefik.http.routers.rustpad.tls=true"
      - "traefik.http.routers.rustpad.tls.certresolver=letsencrypt"
      - "traefik.http.routers.rustpad.middlewares=compress,errors,secure,rustpad"
      - "traefik.http.services.rustpad.loadbalancer.server.port=3030"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/rustpad/data
        target: /app

  ### https://hub.docker.com/r/sflow/prometheus
  sflow-collector:
    image: sflow/prometheus:latest
    container_name: sflow-collector
    restart: unless-stopped
    environment:
      - "RTMEM=2G"
    command:
      - "-Dsnmp.ifname=yes"
      - "-Dgeo.country=resources/config/GeoLite2-Country.mmdb"
    labels:
      - "flame.type=application"
      - "flame.name=sFlow Collector"
      - "flame.url=https://flows.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.sflow.headers.contentSecurityPolicy=default-src 'self'"
      - "traefik.http.routers.sflow.rule=Host(`flows.seedno.de`)"
      - "traefik.http.routers.sflow.entrypoints=https"
      - "traefik.http.routers.sflow.service=sflow"
      - "traefik.http.routers.sflow.tls=true"
      - "traefik.http.routers.sflow.tls.certresolver=letsencrypt"
      - "traefik.http.routers.sflow.middlewares=adminauth,compress,errors,secure,sflow,whitelist@file"
      - "traefik.http.services.sflow.loadbalancer.server.port=8008"
    ports:
      - "10.25.0.1:6343:6343/udp"
    networks:
      - traefik
      - prometheus

  ### https://github.com/sflow/host-sflow
  sflow-exporter:
    image: sflow/host-sflow:latest
    container_name: sflow-exporter
    restart: unless-stopped
    environment:
      - "COLLECTOR=flows.seedno.de"
      - "DROPMON=enable"
      - "NET=wan"
      - "POLLING=20"
      - "PORT=6343"
      - "SAMPLING=1000"
    network_mode: host

  ### https://github.com/oetiker/SmokePing
  smokeping:
    image: ghcr.io/linuxserver/smokeping:latest
    container_name: smokeping
    restart: unless-stopped
    environment:
      - "PUID=${UID:?not set}"
      - "PGID=${GID:?not set}"
      - "TZ=${TIMEZONE:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Smokeping"
      - "flame.url=https://ping.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.smokeping.headers.contentSecurityPolicy=default-src 'self'; img-src 'self' data:"
      - "traefik.http.routers.smokeping.rule=Host(`ping.seedno.de`)"
      - "traefik.http.routers.smokeping.entrypoints=https"
      - "traefik.http.routers.smokeping.service=smokeping"
      - "traefik.http.routers.smokeping.tls=true"
      - "traefik.http.routers.smokeping.tls.certresolver=letsencrypt"
      - "traefik.http.routers.smokeping.middlewares=compress,errors,secure,smokeping,whitelist@file"
      - "traefik.http.services.smokeping.loadbalancer.server.port=80"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/smokeping/config
        target: /config
        read_only: true
      - type: bind
        source: /docker/smokeping/data
        target: /data

  ### https://github.com/RobinLinus/snapdrop
  ## Note: "proxy_set_header X-Forwarded-for $remote_addr;" must be commented out in /config/nginx/site-confs/default, due to how traefik handles the header value
  snapdrop:
    image: ghcr.io/linuxserver/snapdrop:latest
    container_name: snapdrop
    restart: unless-stopped
    environment:
      - "PUID=${UID:?not set}"
      - "PGID=${GID:?not set}"
      - "TZ=${TIMEZONE:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Snapdrop"
      - "flame.url=https://drop.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.snapdrop.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'sha256-biLFinpqYMtWHmXfkA1BPeCY0/fNt46SAZ+BBk5YUog='; style-src-attr 'self' 'unsafe-inline'"
      - "traefik.http.routers.snapdrop.rule=Host(`drop.seedno.de`)"
      - "traefik.http.routers.snapdrop.entrypoints=https"
      - "traefik.http.routers.snapdrop.service=snapdrop"
      - "traefik.http.routers.snapdrop.tls=true"
      - "traefik.http.routers.snapdrop.tls.certresolver=letsencrypt"
      - "traefik.http.routers.snapdrop.middlewares=compress,errors,secure,snapdrop"
      - "traefik.http.services.snapdrop.loadbalancer.server.port=80"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/snapdrop/config
        target: /config

  ### https://github.com/syncthing/syncthing
  syncthing:
    image: ghcr.io/linuxserver/syncthing:amd64-latest
    container_name: syncthing
    restart: unless-stopped
    environment:
      - "PUID=${UID:?not set}"
      - "PGID=${GID:?not set}"
      - "TZ=${TIMEZONE:?not set}"
      - "UMASK_SET=022"
    labels:
      - "flame.type=application"
      - "flame.name=Syncthing"
      - "flame.url=https://sync.crimson.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.routers.syncthing.rule=Host(`sync.crimson.seedno.de`)"
      - "traefik.http.routers.syncthing.entrypoints=https"
      - "traefik.http.routers.syncthing.service=syncthing"
      - "traefik.http.routers.syncthing.tls=true"
      - "traefik.http.routers.syncthing.tls.certresolver=letsencrypt"
      - "traefik.http.routers.syncthing.middlewares=adminauth,compress,errors,secure,whitelist@file"
      - "traefik.http.services.syncthing.loadbalancer.server.port=8384"
    ports:
      - "22000:22000"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/syncthing/config
        target: /config
      - type: bind
        source: /home/sinc/Dropbox
        target: /home/sinc/Dropbox
      - type: bind
        source: /storage
        target: /storage
      - type: bind
        source: /var/www/html
        target: /var/www/html

  ### https://github.com/RblSb/SyncTube
  synctube:
    image: oci.seedno.de/seednode/synctube:latest
    container_name: synctube
    restart: unless-stopped
    labels:
      - "flame.type=application"
      - "flame.name=Synctube"
      - "flame.url=https://synctube.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.synctube.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline' https://rsms.me; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://www.youtube.com; font-src 'self' https://rsms.me; connect-src 'self' https://cdn.jsdelivr.net https://www.googleapis.com; media-src 'self' https://www.googleapis.com; frame-src 'self' https://www.youtube.com"
      - "traefik.http.routers.synctube.rule=Host(`synctube.seedno.de`)"
      - "traefik.http.routers.synctube.entrypoints=https"
      - "traefik.http.routers.synctube.service=synctube"
      - "traefik.http.routers.synctube.tls=true"
      - "traefik.http.routers.synctube.tls.certresolver=letsencrypt"
      - "traefik.http.routers.synctube.middlewares=compress,errors,secure,synctube"
      - "traefik.http.services.synctube.loadbalancer.server.port=4200"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/synctube/config
        target: /usr/src/app/user

  ### https://github.com/beardedio/terraria
  terraria:
    image: beardedio/terraria:tshock-latest
    container_name: terraria
    restart: unless-stopped
    tty: true
    stdin_open: true
    environment:
      - "world=Seedno_de.wld"
    networks:
      - terraria
    ports:
      - "7777:7777"
    volumes:
      - type: bind
        source: /docker/terraria/data
        target: /config

  ### https://github.com/TES3MP/server-container
  ## To make a player admin, edit /server/data/data/player/.json,
  ## and change "staffRank" to 2 and "consoleAllowed" to "true" under "settings"
  tes3mp:
    image: tes3mp/server:0.8.1
    container_name: tes3mp
    restart: unless-stopped
    environment:
      - "TES3MP_SERVER_HOSTNAME=Seedno.de"
      - "TES3MP_SERVER_MAXIMUM_PLAYERS=16"
      - "TES3MP_SERVER_PASSWORD=${TES3MP_SERVER_PASSWORD:?not set}"
      - "TES3MP_SERVER_PORT=25561"
    ports:
      - "25561:25561/udp"
    volumes:
      - type: bind
        source: /docker/tes3mp/data
        target: /server/data

  ### https://github.com/dutchcoders/transfer.sh
  transfer:
    image: dutchcoders/transfer.sh:latest
    container_name: transfer
    restart: unless-stopped
    environment:
      - "BASEDIR=/files/"
      - "CLAMAV_HOST=none"
      - "HTTP_AUTH_USER=${TRANSFER_HTTP_AUTH_USER:?not set}"
      - "HTTP_AUTH_PASS=${TRANSFER_HTTP_AUTH_PASS:?not set}"
      - "LOG=/logs/transfer.sh.log"
      - "PURGE_DAYS=3"
      - "PURGE_INTERVALS=4"
    command:
      - "--provider=local"
    labels:
      - "flame.type=application"
      - "flame.name=Transfer.sh"
      - "flame.url=https://transfer.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.transfer.headers.contentSecurityPolicy=default-src 'self'; img-src 'self' data:"
      - "traefik.http.routers.transfer.rule=Host(`transfer.seedno.de`)"
      - "traefik.http.routers.transfer.entrypoints=https"
      - "traefik.http.routers.transfer.service=transfer"
      - "traefik.http.routers.transfer.tls=true"
      - "traefik.http.routers.transfer.tls.certresolver=letsencrypt"
      - "traefik.http.routers.transfer.middlewares=compress,errors,secure,transfer"
      - "traefik.http.services.transfer.loadbalancer.server.port=8080"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/transfer/data
        target: /files
      - type: bind
        source: /docker/transfer/logs
        target: /logs

  ### https://github.com/linuxserver/docker-unifi-controller
  unifi:
    image: jacobalberty/unifi:latest
    container_name: unifi
    restart: unless-stopped
    environment:
    - "BIND_PRIV=false"
    - "RUNAS_UID0=false"
    - "UNIFI_UID=${UID:?not set}"
    - "UNIFI_GID=${GID:?not set}"
    - "TZ=${TIMEZONE:?not set}"
    labels:
      - "flame.type=application"
      - "flame.name=Unifi Controller"
      - "flame.url=https://unifi.seedno.de"
    networks:
      - traefik
    ports:
      - "3478:3478/udp"
      - "8080:8080"
    volumes:
      - type: bind
        source: /docker/unifi/data
        target: /unifi

  ### https://github.com/dani-garcia/vaultwarden
  vaultwarden:
    image: vaultwarden/server:alpine
    container_name: vaultwarden
    restart: unless-stopped
    depends_on:
      - vaultwarden-db
    environment:
      - "DATABASE_URL=postgresql://${VAULTWARDEN_DATABASE_USER:?not set}:${VAULTWARDEN_DATABASE_PASS:?not set}@vaultwarden-db:5432/${VAULTWARDEN_DATABASE_NAME:?not set}"
      - "DOMAIN=https://vault.seedno.de"
      - "WEBSOCKET_ENABLED=false"
      - "SIGNUPS_ALLOWED=false"
      - "INVITATIONS_ALLOWED=false"
      - "SHOW_PASSWORD_HINT=false"
      - "TRASH_AUTO_DELETE_DAYS=30"
      - "LOG_FILE=/data/vaultwarden.log"
      - "ROCKET_PORT=80"
    labels:
      - "flame.type=application"
      - "flame.name=Vaultwarden"
      - "flame.url=https://vault.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.vaultwarden.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; img-src 'self' data:"
      - "traefik.http.routers.vaultwarden.rule=Host(`vault.seedno.de`)"
      - "traefik.http.routers.vaultwarden.entrypoints=https"
      - "traefik.http.routers.vaultwarden.service=vaultwarden"
      - "traefik.http.routers.vaultwarden.tls=true"
      - "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
      - "traefik.http.routers.vaultwarden.middlewares=compress,errors,secure,vaultwarden"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
    networks:
      - traefik
      - vaultwarden
    volumes:
      - type: bind
        source: /docker/vaultwarden/data
        target: /data

  vaultwarden-db:
    image: postgres:13-alpine
    container_name: vaultwarden-db
    restart: unless-stopped
    environment:
      - "POSTGRES_DB=${VAULTWARDEN_DATABASE_NAME:?not set}"
      - "POSTGRES_USER=${VAULTWARDEN_DATABASE_USER:?not set}"
      - "POSTGRES_PASSWORD=${VAULTWARDEN_DATABASE_PASS:?not set}"
    networks:
      - vaultwarden
    volumes:
      - type: bind
        source: /docker/vaultwarden/database
        target: /var/lib/postgresql/data

  ### https://github.com/lovasoa/whitebophir
  whitebophir:
    image: lovasoa/wbo:latest
    container_name: whitebophir
    restart: unless-stopped
    labels:
      - "flame.type=application"
      - "flame.name=Whitebophir"
      - "flame.url=https://whiteboard.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.whitebophir.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; img-src 'self' data:"
      - "traefik.http.routers.whitebophir.rule=Host(`whiteboard.seedno.de`)"
      - "traefik.http.routers.whitebophir.entrypoints=https"
      - "traefik.http.routers.whitebophir.service=whitebophir"
      - "traefik.http.routers.whitebophir.tls=true"
      - "traefik.http.routers.whitebophir.tls.certresolver=letsencrypt"
      - "traefik.http.routers.whitebophir.middlewares=compress,errors,secure,whitebophir"
      - "traefik.http.services.whitebophir.loadbalancer.server.port=80"
    networks:
      - traefik
    volumes:
      - type: bind
        source: /docker/whitebophir/data
        target: /opt/app/server-data

  ### https://hub.docker.com/r/containous/whoami
  whoami:
    image: containous/whoami:latest
    container_name: whoami
    restart: unless-stopped
    labels:
      - "flame.type=application"
      - "flame.name=WhoAmI"
      - "flame.url=https://whoami.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.middlewares.whoami.headers.contentSecurityPolicy=default-src 'self'"
      - "traefik.http.routers.whoami.rule=Host(`whoami.seedno.de`)"
      - "traefik.http.routers.whoami.entrypoints=https"
      - "traefik.http.routers.whoami.service=whoami"
      - "traefik.http.routers.whoami.tls=true"
      - "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
      - "traefik.http.routers.whoami.middlewares=compress,errors,secure,ratelimit,whoami"
      - "traefik.http.services.whoami.loadbalancer.server.port=80"
    networks:
      - traefik

  ### https://www.xbrowsersync.org/
  xbrowsersync:
    image: xbrowsersync/api:latest
    container_name: xbrowsersync
    restart: unless-stopped
    depends_on:
      - xbrowsersync-db
    environment:
      - "XBROWSERSYNC_DB_PWD=${XBROWSERSYNC_DATABASE_PASS:?not set}"
      - "XBROWSERSYNC_DB_USER=${XBROWSERSYNC_DATABASE_USER:?not set}"
    healthcheck:
      test: [ "CMD", "node", "/usr/src/api/healthcheck.js" ]
      interval: "1m"
      timeout: "10s"
      retries: 5
      start_period: "30s"
    labels:
      - "flame.type=application"
      - "flame.name=xBrowserSync"
      - "flame.url=https://browser-sync.seedno.de"
      - "traefik.enable=true"
      - "traefik.http.routers.xbrowsersync.rule=Host(`browser-sync.seedno.de`)"
      - "traefik.http.routers.xbrowsersync.entrypoints=https"
      - "traefik.http.routers.xbrowsersync.service=xbrowsersync"
      - "traefik.http.routers.xbrowsersync.tls=true"
      - "traefik.http.routers.xbrowsersync.tls.certresolver=letsencrypt"
      - "traefik.http.routers.xbrowsersync.middlewares=compress,errors,secure"
      - "traefik.http.services.xbrowsersync.loadbalancer.server.port=8080"
    networks:
      - traefik
      - xbrowsersync
    volumes:
      - type: bind
        source: /docker/xbrowsersync/config/settings.json
        target: /usr/src/api/config/settings.json
      - type: bind
        source: /docker/xbrowsersync/config/healthcheck.js
        target: /usr/src/api/healthcheck.js

  xbrowsersync-db:
    image: mongo:latest
    container_name: xbrowsersync-db
    restart: unless-stopped
    environment:
      - "MONGO_INITDB_DATABASE=${XBROWSERSYNC_DATABASE_NAME:?not set}"
      - "MONGO_INITDB_ROOT_PASSWORD=${XBROWSERSYNC_DATABASE_PASS:?not set}"
      - "MONGO_INITDB_ROOT_USERNAME=${XBROWSERSYNC_DATABASE_USER:?not set}"
      - "XBS_DB_NAME=${XBROWSERSYNC_DATABASE_NAME:?not set}"
      - "XBS_DB_PASSWORD=${XBROWSERSYNC_DATABASE_PASS:?not set}"
      - "XBS_DB_USERNAME=${XBROWSERSYNC_DATABASE_USER:?not set}"
    networks:
      - xbrowsersync
    volumes:
      - type: bind
        source: /docker/xbrowsersync/database
        target: /data/db
      - type: bind
        source: /docker/xbrowsersync/backups
        target: /data/backups
      - type: bind
        source: /docker/xbrowsersync/config/mongoconfig.js
        target: /docker-entrypoint-initdb.d/mongoconfig.js

networks:
  traefik:
    name: traefik
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: br_traefik
    ipam:
      driver: default
      config:
        - subnet: 10.160.0.0/22
  archivebox:
    name: archivebox
    internal: true
  bookstack:
    name: bookstack
    internal: true
  elasticsearch:
    name: elasticsearch
    internal: true
  endlessh:
    name: endlessh
  gitea:
    name: gitea
    internal: true
  grafana:
    name: grafana
    internal: true
  guacamole:
    name: guacamole
    internal: true
  mastodon:
    name: mastodon
    internal: true
  paperless-ng:
    name: paperless-ng
    internal: true
  php:
    name: php
    internal: true
  prometheus:
    name: prometheus
    internal: true
  terraria:
    name: terraria
  vaultwarden:
    name: vaultwarden
    internal: true
  xbrowsersync:
    name: xbrowsersync
    internal: true