Harden permissions, close exposed ports, and clean up tasks

- Quote file modes. Unquoted 644/755 were read as decimal, so files
  landed as 1204. Templates holding secrets are now 0640 root:docker.
- Stop publishing Authentik 9000/9443 and Karakeep 3000 (Caddy reaches
  them over the docker network), bind dir2opds to the Tailscale IP, and
  drop Caddy's unused 8448.
- Atuin login uses no_log and only runs once; the installer only runs
  when Atuin is missing.
- Make chsh, ufw-docker install, the bat symlink and Atuin dotfiles
  idempotent.
- Back up the Paperless data volume (SQLite DB), stopping Paperless
  during the backup.
- Close public sign-up on kosync and ByteStash.
- Replace hardcoded IPs and domains with variables.
- Remove teardown tasks and templates for 9 retired services.
- Fix the dockcheck comment: it auto-updates labelled containers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Phil
2026-10-29 21:03:54 -06:00
co-authored by Claude Opus 5.5
parent dea3b29b6b
commit c0145dd5a2
65 changed files with 154 additions and 600 deletions
@@ -10,7 +10,7 @@ services:
- TZ=America/Denver
- DISABLE_SSRF_REQUEST_FILTER=1
extra_hosts:
- '{{ subdomains.auth }}:172.20.0.5'
- '{{ subdomains.auth }}:{{ docker.hairpin_ip }}'
labels:
glance.name: Audiobookshelf
glance.icon: si:audiobookshelf
@@ -51,16 +51,13 @@ services:
- ./custom-templates:/templates
env_file:
- .env
ports:
- "${COMPOSE_PORT_HTTP:-9000}:9000"
- "${COMPOSE_PORT_HTTPS:-9443}:9443"
depends_on:
- postgresql
- trout
labels:
glance.name: Authentik
glance.icon: si:authentik
glance.url: https://auth.thesatelliteoflove.com/
glance.url: https://{{ subdomains.auth }}/
glance.description: Authentication server
glance.id: authentik
worker:
@@ -103,4 +100,4 @@ volumes:
networks:
default:
external: true
name: lava
name: {{ docker.network_name }}
@@ -8,7 +8,7 @@ services:
environment:
JWT_SECRET: "{{ vault_bytestash.jwt_secret }}"
TOKEN_EXPIRY: "24h"
ALLOW_NEW_ACCOUNTS: "true"
ALLOW_NEW_ACCOUNTS: "false"
DEBUG: "false"
DISABLE_ACCOUNTS: "false"
DISABLE_INTERNAL_ACCOUNTS: "false"
@@ -6,8 +6,6 @@ services:
- "80:80"
- "443:443"
- "443:443/udp"
- "8448:8448"
- "8448:8448/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./site:/srv
@@ -1,23 +0,0 @@
services:
codeserver:
stdin_open: true
tty: true
labels:
glance.name: Code Server
glance.icon: si:vscodium
glance.url: https://{{ subdomains.code }}/
glance.description: Code Server
mag37.dockcheck.update: true
container_name: codeserver
volumes:
- home:/home
environment:
- DOCKER_USER=$USER
image: codercom/code-server:latest
volumes:
home:
driver: local
networks:
default:
external: true
name: {{ docker.network_name }}
@@ -4,7 +4,7 @@ services:
image: ghcr.io/dubyte/dir2opds:v1.9.0
container_name: dir2opds
ports:
- "8080:8080"
- "{{ network.docker_host_ip }}:8080:8080"
volumes:
- ./books:/books
command:
+1 -1
View File
@@ -5,7 +5,7 @@ services:
labels:
glance.name: Dockge
glance.icon: si:docker
glance.url: http://netcup.porgy-porgy.ts.net:5001
glance.url: http://{{ network.tailscale_hostname }}:5001
glance.description: Docker management
ports:
# Host Port : Container Port
@@ -1,27 +0,0 @@
services:
ghost:
image: ghost:5-alpine
restart: unless-stopped
environment:
- database__client=sqlite3
- database__connection__filename=/var/lib/ghost/content/data/ghost.db
- database__useNullAsDefault=true
- url=https://{{ subdomains.phlog }}
volumes:
- ghost:/var/lib/ghost/content
extra_hosts:
- '{{ subdomains.phlog }}:172.20.0.5'
labels:
glance.name: Ghost
glance.icon: si:ghost
glance.url: https://{{ subdomains.phlog }}/
glance.description: Photo Blog
mag37.dockcheck.update: true
volumes:
ghost:
driver: local
networks:
default:
external: true
name: {{ docker.network_name }}
+2 -2
View File
@@ -8,8 +8,8 @@ services:
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
extra_hosts:
- '{{ primary_domain }}:172.20.0.5'
- '{{ subdomains.watcher }}:172.20.0.5'
- '{{ primary_domain }}:{{ docker.hairpin_ip }}'
- '{{ subdomains.watcher }}:{{ docker.hairpin_ip }}'
labels:
glance.name: Glance
glance.icon: si:homepage
@@ -1,30 +0,0 @@
services:
grocy:
image: lscr.io/linuxserver/grocy:latest
container_name: grocy
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/Denver
volumes:
- ./config:/config
extra_hosts:
- "host.docker.internal:host-gateway"
- "{{ subdomains.auth }}:{{ docker.hairpin_ip }}"
labels:
glance.name: Grocy
glance.icon: si:grocyapp
glance.url: https://{{ subdomains.grocy }}/
glance.description: Kitchen ERP and inventory management
glance.id: grocy
mag37.dockcheck.update: true
volumes:
grocy_config:
driver: local
networks:
default:
external: true
name: {{ docker.network_name }}
@@ -5,8 +5,6 @@ services:
restart: unless-stopped
volumes:
- data:/data
ports:
- 3000:3000
env_file:
- .env
extra_hosts:
@@ -1,32 +0,0 @@
services:
kanboard:
image: kanboard/kanboard:latest
container_name: kanboard
restart: unless-stopped
environment:
- PLUGIN_INSTALLER=true
- DB_DRIVER=sqlite
volumes:
- kanboard_data:/var/www/app/data
- kanboard_plugins:/var/www/app/plugins
extra_hosts:
- "host.docker.internal:host-gateway"
- "{{ subdomains.auth }}:{{ docker.hairpin_ip }}"
labels:
glance.name: Kanboard
glance.icon: si:kanboard
glance.url: https://{{ subdomains.kanboard }}/
glance.description: Project management and Kanban boards
glance.id: kanboard
mag37.dockcheck.update: true
volumes:
kanboard_data:
driver: local
kanboard_plugins:
driver: local
networks:
default:
external: true
name: {{ docker.network_name }}
+1 -1
View File
@@ -4,7 +4,7 @@ services:
image: koreader/kosync:latest
container_name: kosync
environment:
- ENABLE_USER_REGISTRATION=true
- ENABLE_USER_REGISTRATION=false
volumes:
- ./logs/app:/app/koreader-sync-server/logs
- ./logs/redis:/var/log/redis
@@ -1,48 +0,0 @@
services:
mmdl:
image: intriin/mmdl:latest
container_name: mmdl
restart: unless-stopped
depends_on:
- mmdl_db
env_file:
- .env.local
extra_hosts:
- "host.docker.internal:host-gateway"
- "{{ subdomains.auth }}:{{ docker.hairpin_ip }}"
- "{{ subdomains.cal }}:{{ docker.hairpin_ip }}"
labels:
glance.name: MMDL
glance.icon: si:task
glance.url: https://{{ subdomains.tasks }}/
glance.description: Task and calendar management
glance.id: mmdl
mag37.dockcheck.update: true
mmdl_db:
image: mysql:8.0
container_name: mmdl_db
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_DATABASE: mmdl
MYSQL_USER: mmdl
MYSQL_PASSWORD: "{{ vault_mmdl.mysql_password }}"
MYSQL_ROOT_PASSWORD: "{{ vault_mmdl.mysql_root_password }}"
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_ROOT_HOST: "%"
volumes:
- mmdl_db:/var/lib/mysql
labels:
glance.parent: mmdl
glance.name: DB
mag37.dockcheck.update: true
volumes:
mmdl_db:
driver: local
networks:
default:
external: true
name: {{ docker.network_name }}
-42
View File
@@ -1,42 +0,0 @@
# Database Configuration
DB_HOST=mmdl_db
DB_USER=mmdl
DB_PASS={{ vault_mmdl.mysql_password }}
DB_PORT=3306
DB_DIALECT=mysql
DB_CHARSET=utf8mb4
DB_NAME=mmdl
# Encryption
AES_PASSWORD={{ vault_mmdl.aes_password }}
# SMTP Settings
SMTP_HOST={{ smtp.host }}
SMTP_USERNAME={{ smtp.username }}
SMTP_PASSWORD={{ vault_smtp.password }}
SMTP_FROM=tasks@{{ email_domains.updates }}
SMTP_PORT=587
SMTP_SECURE=true
# Authentication
USE_NEXT_AUTH=true
NEXTAUTH_URL=https://{{ subdomains.tasks }}
NEXTAUTH_SECRET={{ vault_mmdl.nextauth_secret }}
# Authentik OIDC Configuration
AUTHENTIK_ISSUER=https://{{ subdomains.auth }}/application/o/mmdl
AUTHENTIK_CLIENT_ID={{ vault_mmdl.oidc.client_id }}
AUTHENTIK_CLIENT_SECRET={{ vault_mmdl.oidc.client_secret }}
# User and Session Management
ALLOW_USER_REGISTRATION=false
MAX_CONCURRENT_LOGINS=3
OTP_VALIDITY_PERIOD=300
SESSION_VALIDITY_PERIOD=30
# Application Settings
API_URL=https://{{ subdomains.tasks }}
DEBUG_MODE=false
TEST_MODE=false
NEXT_API_DEBUG_MODE=false
SUBTASK_RECURSION_DEPTH=5
@@ -1,30 +0,0 @@
services:
palmr:
image: kyantech/palmr:latest
restart: unless-stopped
environment:
DISABLE_FILESYSTEM_ENCRYPTION: "false"
ENCRYPTION_KEY: "{{ vault_palmr.encryption_key }}"
PALMR_UID: "1000"
PALMR_GID: "1000"
SECURE_SITE: "true"
DEFAULT_LANGUAGE: "en-US"
TRUST_PROXY: "true"
extra_hosts:
- "{{ subdomains.auth }}:{{ docker.hairpin_ip }}"
labels:
glance.name: Palmr
glance.icon: si:files
glance.url: "https://{{ subdomains.files }}/"
glance.description: File sharing and storage
glance.id: palmr
mag37.dockcheck.update: true
volumes:
- palmr_data:/app/server
volumes:
palmr_data:
driver: local
networks:
default:
external: true
name: "{{ docker.network_name }}"
@@ -12,6 +12,8 @@ services:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
restart: unless-stopped
labels:
# Stop during backup so the SQLite database in the data volume is consistent
docker-volume-backup.stop-during-backup: true
glance.name: Paperless NGX
glance.icon: si:paperlessngx
glance.url: https://{{ subdomains.paper }}/
@@ -75,6 +77,7 @@ services:
AWS_SECRET_ACCESS_KEY: {{ vault_backup.secret_access_key }}
volumes:
- media:/backup/pngx-app-backup:ro
- data:/backup/pngx-data-backup:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
volumes:
@@ -1,22 +0,0 @@
services:
pinchflat:
environment:
- TZ=America/Denver
ports:
- 100.70.169.99:8945:8945
volumes:
- ./config:/config
- data:/downloads
image: ghcr.io/kieraneglin/pinchflat:latest
labels:
glance.name: Pinchflat
glance.icon: si:youtube
glance.url: http://netcup.porgy-porgy.ts.net:8945
glance.description: Youtube interface
glance.id: pinchflat
volumes:
data:
networks:
default:
external: true
name: lava
@@ -1,21 +0,0 @@
services:
pinry:
volumes:
- pinry:/data
labels:
glance.name: Pinry
glance.icon: si:pinterest
glance.url: https://{{ subdomains.pin }}
glance.description: Pinterest clone
glance.id: pinterest
environment:
- SECRET_KEY=no2254XiwYFWDnt2UW6wraSbVPRdHx8wVIeBh3jeYcI=
- ALLOW_NEW_REGISTRATIONS=False
image: getpinry/pinry
volumes:
pinry:
driver: local
networks:
default:
external: true
name: {{ docker.network_name }}
@@ -1,96 +0,0 @@
services:
postiz:
image: ghcr.io/gitroomhq/postiz-app:latest
container_name: postiz
restart: always
environment:
# You must change these. Replace `postiz.your-server.com` with your DNS name - what your web browser sees.
MAIN_URL: "https://{{ subdomains.post }}"
FRONTEND_URL: "https://{{ subdomains.post }}"
NEXT_PUBLIC_BACKEND_URL: "https://{{ subdomains.post }}/api"
JWT_SECRET: "TShr6Fdcwf67wIhuUvg0gOsJbdcQmgMiJl5kUh6JCfY="
# These defaults are probably fine, but if you change your user/password, update it in the
# postiz-postgres or postiz-redis services below.
DATABASE_URL: "postgresql://postiz-user:postiz-password@postiz-postgres:5432/postiz-db-local"
REDIS_URL: "redis://postiz-redis:6379"
BACKEND_INTERNAL_URL: "http://localhost:3000"
IS_GENERAL: "true" # Required for self-hosting.
# The container images are pre-configured to use /uploads for file storage.
# You probably should not change this unless you have a really good reason!
STORAGE_PROVIDER: "local"
UPLOAD_DIRECTORY: "/uploads"
NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads"
# Social keys
LINKEDIN_CLIENT_ID: "86q7ksc8q5pai3"
LINKEDIN_CLIENT_SECRET: {{ vault_postiz.linkedin_secret }}
volumes:
- postiz-config:/config/
- postiz-uploads:/uploads/
depends_on:
postiz-postgres:
condition: service_healthy
postiz-redis:
condition: service_healthy
labels:
glance.name: Postiz
glance.url: https://{{ subdomains.post }}/
glance.description: Social media scheduler
glance.id: postiz
mag37.dockcheck.update: true
postiz-postgres:
image: postgres:14.5
container_name: postiz-postgres
restart: always
environment:
POSTGRES_PASSWORD: postiz-password
POSTGRES_USER: postiz-user
POSTGRES_DB: postiz-db-local
volumes:
- postgres-volume:/var/lib/postgresql/data
healthcheck:
test: pg_isready -U postiz-user -d postiz-db-local
interval: 10s
timeout: 3s
retries: 3
labels:
glance.parent: postiz
glance.name: DB
mag37.dockcheck.update: true
postiz-redis:
image: redis:7.2
container_name: postiz-redis
restart: always
healthcheck:
test: redis-cli ping
interval: 10s
timeout: 3s
retries: 3
volumes:
- postiz-redis-data:/data
labels:
glance.parent: postiz
glance.name: Redis
mag37.dockcheck.update: true
volumes:
postgres-volume:
external: false
postiz-redis-data:
external: false
postiz-config:
external: false
postiz-uploads:
external: false
networks:
default:
external: true
name: {{ docker.network_name }}
@@ -6,7 +6,7 @@ services:
labels:
glance.name: Syncthing
glance.icon: si:syncthing
glance.url: https://netcup.porgy-porgy.ts.net:8384
glance.url: https://{{ network.tailscale_hostname }}:8384
glance.description: Syncthing core
glance.id: Syncthing
mag37.dockcheck.update: true
@@ -16,10 +16,10 @@ services:
volumes:
- home:/var/syncthing
ports:
- 100.70.169.99:8384:8384 # Web UI
- 100.70.169.99:22000:22000/tcp # TCP file transfers
- 100.70.169.99:22000:22000/udp # QUIC file transfers
- 100.70.169.99:21027:21027/udp # Receive local discovery broadcasts
- {{ network.docker_host_ip }}:8384:8384 # Web UI
- {{ network.docker_host_ip }}:22000:22000/tcp # TCP file transfers
- {{ network.docker_host_ip }}:22000:22000/udp # QUIC file transfers
- {{ network.docker_host_ip }}:21027:21027/udp # Receive local discovery broadcasts
restart: unless-stopped
healthcheck:
test: curl -fkLsS -m 2 127.0.0.1:8384/rest/noauth/health | grep -o --color=never OK || exit 1