Initial commit: 23 docker stacks for GitOps deployment

Stacks included:
- Infrastructure: traefik, authentik, gitea, registry, watchtower, dockge
- Monitoring: smokeping, changedetection
- Apps: ghost, gollum, wallabag, radicale, invidious, xbackbone, filebrowser, syncthing, zerotier
- Custom: obsidian-tools, memento, perilous, ramz, bookclub, brain

🤖 Generated with Claude Code
This commit is contained in:
2025-12-31 13:29:43 -05:00
commit 4dbb0b9180
56 changed files with 1390 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
DOMAIN=${DOMAIN}
AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}
AUTHENTIK_PG_PASS=${AUTHENTIK_PG_PASS}

View File

@@ -0,0 +1,75 @@
services:
server:
image: ghcr.io/goauthentik/server:latest
container_name: authentik-server
restart: unless-stopped
command: server
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_PG_PASS}
volumes:
- ./media:/media
- ./custom-templates:/templates
env_file:
- .env
depends_on:
- postgresql
- redis
networks:
- web
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.authentik.entrypoints=https"
- "traefik.http.routers.authentik.rule=Host(`authentik.${DOMAIN}`)"
- "traefik.http.routers.authentik.tls.certresolver=http"
- "traefik.http.services.authentik.loadbalancer.server.port=9000"
worker:
image: ghcr.io/goauthentik/server:latest
container_name: authentik-worker
restart: unless-stopped
command: worker
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_PG_PASS}
volumes:
- ./media:/media
- ./certs:/certs
- ./custom-templates:/templates
env_file:
- .env
depends_on:
- postgresql
- redis
postgresql:
image: postgres:15-alpine
container_name: authentik-postgres
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${AUTHENTIK_PG_PASS}
POSTGRES_USER: authentik
POSTGRES_DB: authentik
volumes:
- ./postgres:/var/lib/postgresql/data
redis:
image: redis:alpine
container_name: authentik-redis
restart: unless-stopped
command: --save 60 1 --loglevel warning
volumes:
- ./redis:/data
networks:
web:
external: true

View File

@@ -0,0 +1,14 @@
DOMAIN=${DOMAIN}
SMTP_HOST=${BOOKCLUB_SMTP_HOST}
SMTP_PORT=465
SMTP_SECURE=true
SMTP_USER=${BOOKCLUB_SMTP_USER}
SMTP_PASS=${BOOKCLUB_SMTP_PASS}
MAIL_FROM=${BOOKCLUB_MAIL_FROM}
MAIL_TO=${BOOKCLUB_MAIL_TO}
SECRET_PHRASE=${BOOKCLUB_SECRET_PHRASE}
THANK_YOU_URL=/thanks.html
BIND_HOST=0.0.0.0
BIND_PORT=3000
TRUST_PROXY=true
TZ=America/New_York

View File

@@ -0,0 +1,7 @@
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install
COPY server.js ./
EXPOSE 3000
CMD ["node", "server.js"]

View File

@@ -0,0 +1,20 @@
services:
form-mailer:
build: .
container_name: bookclub-form-mailer
restart: unless-stopped
env_file:
- .env
labels:
- "traefik.enable=true"
- "traefik.http.routers.form-mailer.rule=Host(`bookclub.${DOMAIN}`)"
- "traefik.http.routers.form-mailer.entrypoints=https"
- "traefik.http.routers.form-mailer.tls=true"
- "traefik.http.routers.form-mailer.tls.certresolver=http"
- "traefik.http.services.form-mailer.loadbalancer.server.port=3000"
networks:
- web
networks:
web:
external: true

View File

@@ -0,0 +1,13 @@
{
"name": "bookclub-form-mailer",
"version": "1.0.0",
"description": "Simple form mailer service",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2",
"nodemailer": "^6.9.7"
}
}

51
stacks/bookclub/server.js Normal file
View File

@@ -0,0 +1,51 @@
const express = require('express');
const nodemailer = require('nodemailer');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
if (process.env.TRUST_PROXY === 'true') {
app.set('trust proxy', true);
}
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT || '465'),
secure: process.env.SMTP_SECURE === 'true',
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
});
app.post('/submit', async (req, res) => {
const { secret, ...formData } = req.body;
if (secret !== process.env.SECRET_PHRASE) {
return res.status(403).send('Invalid secret phrase');
}
try {
await transporter.sendMail({
from: process.env.MAIL_FROM,
to: process.env.MAIL_TO,
subject: 'New Form Submission',
text: Object.entries(formData)
.map(([key, value]) => `${key}: ${value}`)
.join('\n')
});
res.redirect(process.env.THANK_YOU_URL || '/thanks.html');
} catch (error) {
console.error('Mail error:', error);
res.status(500).send('Failed to send email');
}
});
const port = parseInt(process.env.BIND_PORT || '3000');
const host = process.env.BIND_HOST || '0.0.0.0';
app.listen(port, host, () => {
console.log(`Form mailer listening on ${host}:${port}`);
});

View File

@@ -0,0 +1,3 @@
DOMAIN=${DOMAIN}
BRAIN_SFTP_USER=${BRAIN_SFTP_USER}
BRAIN_SFTP_PASSWORD=${BRAIN_SFTP_PASSWORD}

View File

@@ -0,0 +1,32 @@
services:
web:
image: nginx:latest
container_name: brain
restart: unless-stopped
expose:
- 80
volumes:
- ./public:/usr/share/nginx/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.brain.entrypoints=https"
- "traefik.http.routers.brain.rule=Host(`brain.${DOMAIN}`)"
- "traefik.http.routers.brain.tls.certresolver=http"
- "traefik.http.routers.brain.middlewares=auth@file"
sftp:
image: atmoz/sftp
container_name: brain-sftp
restart: unless-stopped
volumes:
- ./public:/home/commander/upload
ports:
- "2232:22"
command: ${BRAIN_SFTP_USER}:${BRAIN_SFTP_PASSWORD}:1001
networks:
web:
external: true

View File

@@ -0,0 +1 @@
DOMAIN=${DOMAIN}

View File

@@ -0,0 +1,40 @@
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io
container_name: changedetection
hostname: changedetection
restart: unless-stopped
volumes:
- ./data:/datastore
environment:
- PLAYWRIGHT_DRIVER_URL=ws://playwright-chrome:3000/?stealth=1&--disable-web-security=true
ports:
- 5000:5000
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.changedetection.entrypoints=https"
- "traefik.http.routers.changedetection.rule=Host(`change.${DOMAIN}`)"
- "traefik.http.routers.changedetection.tls.certresolver=http"
- "traefik.http.routers.changedetection.middlewares=auth@file"
depends_on:
- playwright-chrome
playwright-chrome:
image: dgtlmoon/sockpuppetbrowser:latest
hostname: playwright-chrome
restart: unless-stopped
cap_add:
- SYS_ADMIN
environment:
- SCREEN_WIDTH=1920
- SCREEN_HEIGHT=1024
- SCREEN_DEPTH=16
- MAX_CONCURRENT_CHROME_PROCESSES=10
networks:
- web
networks:
web:
external: true

View File

@@ -0,0 +1 @@
DOMAIN=${DOMAIN}

View File

@@ -0,0 +1,25 @@
services:
dockge:
image: louislam/dockge:1
container_name: dockge
restart: unless-stopped
ports:
- 5001:5001
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/app/data
- /var/core:/var/core
environment:
- DOCKGE_STACKS_DIR=/var/core
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.dockge.rule=Host(`dockge.${DOMAIN}`)"
- "traefik.http.routers.dockge.entrypoints=https"
- "traefik.http.routers.dockge.tls.certresolver=http"
- "traefik.http.services.dockge.loadbalancer.server.port=5001"
networks:
web:
external: true

View File

@@ -0,0 +1,2 @@
DOMAIN=${DOMAIN}
FILE_ROOT=/srv

View File

@@ -0,0 +1,22 @@
services:
filebrowser:
image: filebrowser/filebrowser:latest
container_name: filebrowser
restart: unless-stopped
volumes:
- ./config/filebrowser.db:/database.db
- ${FILE_ROOT:-/srv}:/srv
expose:
- 80
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.filebrowser.entrypoints=https"
- "traefik.http.routers.filebrowser.rule=Host(`files.${DOMAIN}`)"
- "traefik.http.routers.filebrowser.tls.certresolver=http"
- "traefik.http.services.filebrowser.loadbalancer.server.port=80"
networks:
web:
external: true

View File

@@ -0,0 +1,2 @@
DOMAIN=${DOMAIN}
GHOST_DB_PASSWORD=${GHOST_DB_PASSWORD}

View File

@@ -0,0 +1,37 @@
services:
ghost:
image: ghost:4-alpine
container_name: ghost
restart: unless-stopped
expose:
- 2368
networks:
- web
- default
environment:
database__client: mysql
database__connection__host: db
database__connection__user: root
database__connection__password: ${GHOST_DB_PASSWORD}
database__connection__database: ghost
url: https://ghost.${DOMAIN}
volumes:
- ./content:/var/lib/ghost/content
labels:
- "traefik.enable=true"
- "traefik.http.routers.ghost.entrypoints=https"
- "traefik.http.routers.ghost.rule=Host(`ghost.${DOMAIN}`)"
- "traefik.http.routers.ghost.tls.certresolver=http"
db:
image: mysql:5.7
container_name: ghost-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${GHOST_DB_PASSWORD}
volumes:
- ./mysql:/var/lib/mysql
networks:
web:
external: true

View File

@@ -0,0 +1,4 @@
DOMAIN=${DOMAIN}
GITEA_DB_ROOT_PASSWORD=${GITEA_DB_ROOT_PASSWORD}
GITEA_DB_PASSWORD=${GITEA_DB_PASSWORD}
GITEA_RUNNER_TOKEN=${GITEA_RUNNER_TOKEN}

View File

@@ -0,0 +1,58 @@
services:
web:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
volumes:
- ./data:/data
expose:
- "3000"
- "22"
ports:
- "2245:22"
depends_on:
- db
environment:
- DOMAIN=gitea.${DOMAIN}
- SSH_DOMAIN=gitea.${DOMAIN}
- SSH_PORT=2245
- SSH_LISTEN_PORT=22
networks:
- default
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.gitea.entrypoints=https"
- "traefik.http.routers.gitea.rule=Host(`gitea.${DOMAIN}`)"
- "traefik.http.routers.gitea.tls.certresolver=http"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
db:
image: mariadb:10
container_name: gitea-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${GITEA_DB_ROOT_PASSWORD}
- MYSQL_DATABASE=gitea
- MYSQL_USER=gitea
- MYSQL_PASSWORD=${GITEA_DB_PASSWORD}
- MARIADB_AUTO_UPGRADE=1
volumes:
- ./db:/var/lib/mysql
runner:
image: gitea/act_runner
container_name: gitea-runner
restart: unless-stopped
depends_on:
- web
volumes:
- ./runner-data:/data
- /var/run/docker.sock:/var/run/docker.sock
environment:
- GITEA_INSTANCE_URL=https://gitea.${DOMAIN}
- GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_TOKEN}
networks:
web:
external: true

View File

@@ -0,0 +1 @@
DOMAIN=${DOMAIN}

View File

@@ -0,0 +1,24 @@
services:
gollum:
image: gollumwiki/gollum:master
container_name: gollum
restart: unless-stopped
volumes:
- ./config.rb:/etc/gollum/config.rb
- ./wikidata:/wiki
command:
- "--config=/etc/gollum/config.rb"
expose:
- 4567
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.gollum.entrypoints=https"
- "traefik.http.routers.gollum.rule=Host(`gollum.${DOMAIN}`)"
- "traefik.http.routers.gollum.tls.certresolver=http"
- "traefik.http.routers.gollum.middlewares=auth@file"
networks:
web:
external: true

View File

@@ -0,0 +1,5 @@
DOMAIN=${DOMAIN}
INVIDIOUS_DB_PASSWORD=${INVIDIOUS_DB_PASSWORD}
INVIDIOUS_VISITOR_DATA=${INVIDIOUS_VISITOR_DATA}
INVIDIOUS_PO_TOKEN=${INVIDIOUS_PO_TOKEN}
INVIDIOUS_HMAC_KEY=${INVIDIOUS_HMAC_KEY}

View File

@@ -0,0 +1,67 @@
services:
invidious:
image: quay.io/invidious/invidious:latest
container_name: invidious
restart: unless-stopped
ports:
- "3001:3000"
environment:
INVIDIOUS_CONFIG: |
db:
dbname: invidious
user: kemal
password: ${INVIDIOUS_DB_PASSWORD}
host: invidious-db
port: 5432
check_tables: true
signature_server: inv_sig_helper:12999
visitor_data: ${INVIDIOUS_VISITOR_DATA}
po_token: ${INVIDIOUS_PO_TOKEN}
hmac_key: "${INVIDIOUS_HMAC_KEY}"
logging:
options:
max-size: "1G"
max-file: "4"
depends_on:
- invidious-db
labels:
- "traefik.enable=true"
- "traefik.http.services.invidious.loadbalancer.server.port=3000"
- "traefik.http.routers.invidious.entrypoints=https"
- "traefik.http.routers.invidious.rule=Host(`invid.${DOMAIN}`, `i.${DOMAIN}`)"
- "traefik.http.routers.invidious.tls.certresolver=http"
networks:
- web
- default
inv_sig_helper:
image: quay.io/invidious/inv-sig-helper:latest
container_name: inv-sig-helper
command: ["--tcp", "0.0.0.0:12999"]
environment:
- RUST_LOG=info
restart: unless-stopped
cap_drop:
- ALL
read_only: true
security_opt:
- no-new-privileges:true
invidious-db:
image: postgres:14
container_name: invidious-db
restart: unless-stopped
volumes:
- ./postgres:/var/lib/postgresql/data
- ./config/sql:/config/sql
- ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
environment:
POSTGRES_DB: invidious
POSTGRES_USER: kemal
POSTGRES_PASSWORD: ${INVIDIOUS_DB_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
networks:
web:
external: true

View File

@@ -0,0 +1,6 @@
DOMAIN=${DOMAIN}
AUTH_SECRET=${MEMENTO_AUTH_SECRET}
AUTH_AUTHENTIK_CLIENT_ID=${MEMENTO_AUTHENTIK_CLIENT_ID}
AUTH_AUTHENTIK_CLIENT_SECRET=${MEMENTO_AUTHENTIK_CLIENT_SECRET}
AUTH_AUTHENTIK_ISSUER=https://authentik.${DOMAIN}/application/o/memento
AUTH_AUTHENTIK_REDIRECT_URI=https://memento.${DOMAIN}

View File

@@ -0,0 +1,22 @@
services:
app:
image: gitea.${DOMAIN}/knight/memento:latest
container_name: memento
restart: unless-stopped
expose:
- "3002"
env_file:
- .env
networks:
- web
- default
labels:
- "traefik.enable=true"
- "traefik.http.routers.memento.entrypoints=https"
- "traefik.http.routers.memento.rule=Host(`memento.${DOMAIN}`)"
- "traefik.http.routers.memento.tls.certresolver=http"
- "traefik.http.services.memento.loadbalancer.server.port=3002"
networks:
web:
external: true

View File

@@ -0,0 +1 @@
DOMAIN=${DOMAIN}

View File

@@ -0,0 +1,87 @@
services:
obbytodo:
image: gitea.${DOMAIN}/knight/obbytodo:latest
container_name: obbytodo
restart: unless-stopped
expose:
- "3000"
environment:
- PORT=3000
- VAULT_PATH=/vault
- DEFAULT_TASK_FILE=Mobile Tasks.md
- DAILY_DIR=log
- DAILY_PRIMARY_DIR=Control/log
volumes:
- ./vault:/vault
networks:
- web
depends_on:
- syncthing
labels:
- "traefik.enable=true"
- "traefik.docker.network=web"
- "traefik.http.routers.todo-obbytodo.entrypoints=https"
- "traefik.http.routers.todo-obbytodo.rule=Host(`shell.${DOMAIN}`) && PathPrefix(`/todo`)"
- "traefik.http.routers.todo-obbytodo.tls.certresolver=http"
- "traefik.http.routers.todo-obbytodo.middlewares=todo-obbytodo-stripprefix@docker,dashboard-auth@file"
- "traefik.http.routers.todo-obbytodo.priority=100"
- "traefik.http.middlewares.todo-obbytodo-stripprefix.stripPrefix.prefixes=/todo"
- "traefik.http.services.todo-obbytodo.loadbalancer.server.port=3000"
- "traefik.http.routers.todo-obbytodo.service=todo-obbytodo"
- "traefik.http.routers.events-obbytodo.entrypoints=https"
- "traefik.http.routers.events-obbytodo.rule=Host(`shell.${DOMAIN}`) && PathPrefix(`/events`)"
- "traefik.http.routers.events-obbytodo.tls.certresolver=http"
- "traefik.http.routers.events-obbytodo.middlewares=dashboard-auth@file"
- "traefik.http.routers.events-obbytodo.priority=100"
- "traefik.http.routers.events-obbytodo.service=todo-obbytodo"
search-service:
image: gitea.${DOMAIN}/knight/obsidiansearch:latest
container_name: obsidian-search-service
restart: unless-stopped
expose:
- "3033"
environment:
- NODE_ENV=production
- OBSIDIAN_VAULT_PATH=/data
- GRAPH_DATA_PATH=/usr/src/app/graph-data/graph.json
volumes:
- ./vault:/data:ro
- ./graph-data:/usr/src/app/graph-data
depends_on:
- syncthing
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.docker.network=web"
- "traefik.http.routers.shell-secure.entrypoints=https"
- "traefik.http.routers.shell-secure.rule=Host(`shell.${DOMAIN}`)"
- "traefik.http.routers.shell-secure.tls.certresolver=http"
- "traefik.http.routers.shell-secure.middlewares=dashboard-auth@file"
- "traefik.http.services.shell-secure.loadbalancer.server.port=3033"
- "traefik.http.routers.shell-secure.service=shell-secure"
syncthing:
image: lscr.io/linuxserver/syncthing:latest
container_name: obsidian-syncthing
hostname: obsidian-syncthing
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- ./syncthing-config:/config
- ./vault:/data1
ports:
- "8385:8384"
- "22001:22000/tcp"
- "22001:22000/udp"
- "21028:21027/udp"
networks:
- web
networks:
web:
external: true

View File

@@ -0,0 +1 @@
PERILOUS_CODE_SERVER_PASSWORD=${PERILOUS_CODE_SERVER_PASSWORD}

View File

@@ -0,0 +1,7 @@
FROM node:18-alpine
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY server.js ./
EXPOSE 3000
CMD ["node", "server.js"]

View File

@@ -0,0 +1,43 @@
services:
web:
build: .
container_name: perilous-web
restart: unless-stopped
ports:
- "3003:3000"
volumes:
- ./content:/usr/src/app/content
labels:
- "traefik.enable=true"
- "traefik.http.routers.perilous-secure.entrypoints=https,http"
- "traefik.http.routers.perilous-secure.rule=Host(`chapel.perilous.dev`,`forest.perilous.dev`,`castle.perilous.dev`,`siege.perilous.dev`,`pass.perilous.dev`,`perilous.dev`,`www.perilous.dev`,`word.perilous.dev`,`mirror.perilous.dev`,`request.perilous.dev`,`the.chapel.perilous.dev`,`the.forest.perilous.dev`,`the.castle.perilous.dev`,`the.siege.perilous.dev`,`the.pass.perilous.dev`,`the.word.perilous.dev`,`the.mirror.perilous.dev`,`the.request.perilous.dev`,`the.adventure.perilous.dev`,`the.cs.perilous.dev`,`the.gallery.perilous.dev`,`gallery.perilous.dev`,`the.perilous.dev`,`ring.perilous.dev`,`the.ring.perilous.dev`,`autumn.perilous.dev`,`the.autumn.perilous.dev`)"
- "traefik.http.routers.perilous-secure.tls.certresolver=http"
networks:
- web
code-server:
image: lscr.io/linuxserver/code-server:latest
container_name: perilous-code-server
restart: unless-stopped
volumes:
- ./content:/home/project
- ./config:/config
ports:
- "8180:8443"
environment:
- PASSWORD=${PERILOUS_CODE_SERVER_PASSWORD}
- PUID=1000
- PGID=1000
- PROXY_DOMAIN=cs.perilous.dev
- DEFAULT_WORKSPACE=/home/project
labels:
- "traefik.enable=true"
- "traefik.http.routers.pcs-secure.entrypoints=https,http"
- "traefik.http.routers.pcs-secure.rule=Host(`cs.perilous.dev`)"
- "traefik.http.routers.pcs-secure.tls.certresolver=http"
networks:
- web
networks:
web:
external: true

View File

@@ -0,0 +1,12 @@
{
"name": "perilous",
"version": "1.0.0",
"description": "Perilous static site server",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}

24
stacks/perilous/server.js Normal file
View File

@@ -0,0 +1,24 @@
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
const PORT = process.env.PORT || 3000;
const CONTENT_DIR = '/usr/src/app/content';
app.use(express.static(CONTENT_DIR));
app.get('*', (req, res) => {
const filePath = path.join(CONTENT_DIR, req.path);
if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
const indexPath = path.join(filePath, 'index.html');
if (fs.existsSync(indexPath)) {
return res.sendFile(indexPath);
}
}
res.sendFile(path.join(CONTENT_DIR, 'index.html'));
});
app.listen(PORT, () => {
console.log(`Perilous server running on port ${PORT}`);
});

View File

@@ -0,0 +1 @@
DOMAIN=${DOMAIN}

View File

@@ -0,0 +1,35 @@
services:
radicale:
image: tomsquest/docker-radicale
container_name: radicale
restart: unless-stopped
init: true
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- SETUID
- SETGID
- CHOWN
- KILL
healthcheck:
test: curl -f http://127.0.0.1:5232 || exit 1
interval: 30s
retries: 3
expose:
- 5232
networks:
- web
volumes:
- ./data:/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.radicale.entrypoints=https"
- "traefik.http.routers.radicale.rule=Host(`radicale.${DOMAIN}`)"
- "traefik.http.routers.radicale.tls.certresolver=http"
networks:
web:
external: true

13
stacks/ramz/Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY *.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -o ramz .
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/ramz .
COPY static/ ./static/
EXPOSE 3333
CMD ["./ramz"]

View File

@@ -0,0 +1,24 @@
services:
app:
build: .
container_name: ramz
restart: unless-stopped
ports:
- "3333:3333"
environment:
- GIN_MODE=release
volumes:
- ./templates:/root/templates
- ./data:/root/data
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.ramz-secure.entrypoints=https,http"
- "traefik.http.routers.ramz-secure.rule=Host(`parker.ramz.cc`)"
- "traefik.http.routers.ramz-secure.tls.certresolver=http"
- "traefik.http.services.ramz.loadbalancer.server.port=3333"
networks:
web:
external: true

View File

@@ -0,0 +1,2 @@
DOMAIN=${DOMAIN}
REGISTRY_HTTP_SECRET=${REGISTRY_HTTP_SECRET}

View File

@@ -0,0 +1,27 @@
services:
registry:
image: registry:2
container_name: registry
restart: unless-stopped
volumes:
- ./data:/var/lib/registry
- ./auth:/auth
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_HTTP_SECRET: ${REGISTRY_HTTP_SECRET}
expose:
- 5000
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.registry.entrypoints=https"
- "traefik.http.routers.registry.rule=Host(`registry.${DOMAIN}`)"
- "traefik.http.routers.registry.tls.certresolver=http"
- "traefik.http.services.registry.loadbalancer.server.port=5000"
networks:
web:
external: true

View File

@@ -0,0 +1 @@
DOMAIN=${DOMAIN}

View File

@@ -0,0 +1,26 @@
services:
smokeping:
image: lscr.io/linuxserver/smokeping:latest
container_name: smokeping
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- ./config:/config
- ./data:/data
expose:
- 80
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.smokeping.entrypoints=https"
- "traefik.http.routers.smokeping.rule=Host(`smokeping.${DOMAIN}`)"
- "traefik.http.routers.smokeping.tls.certresolver=http"
- "traefik.http.services.smokeping.loadbalancer.server.port=80"
networks:
web:
external: true

View File

@@ -0,0 +1 @@
DOMAIN=${DOMAIN}

View File

@@ -0,0 +1,30 @@
services:
syncthing:
image: lscr.io/linuxserver/syncthing:latest
container_name: syncthing
hostname: syncthing
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- ./config:/config
- ./data:/data
ports:
- "8384:8384"
- "22000:22000/tcp"
- "22000:22000/udp"
- "21027:21027/udp"
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.syncthing.entrypoints=https"
- "traefik.http.routers.syncthing.rule=Host(`syncthing.${DOMAIN}`)"
- "traefik.http.routers.syncthing.tls.certresolver=http"
- "traefik.http.services.syncthing.loadbalancer.server.port=8384"
networks:
web:
external: true

View File

@@ -0,0 +1 @@
ACME_EMAIL=${ACME_EMAIL}

View File

@@ -0,0 +1,25 @@
services:
traefik:
image: traefik:latest
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- web
ports:
- 80:80
- 443:443
- 8080:8080
environment:
- TZ=America/New_York
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./acme.json:/acme.json
- ./conf.d/:/conf.d/
- /var/log:/var/log
networks:
web:
external: true

View File

@@ -0,0 +1,40 @@
global:
checkNewVersion: true
api:
dashboard: true
insecure: true
entryPoints:
http:
address: ":80"
https:
address: ":443"
providers:
providersThrottleDuration: 2s
file:
directory: "/conf.d"
watch: true
docker:
watch: true
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: web
certificatesResolvers:
http:
acme:
email: ${ACME_EMAIL}
storage: acme.json
httpChallenge:
entryPoint: http
log:
level: INFO
filePath: /var/log/traefik.log
format: json
accessLog:
filePath: /var/log/traefik_access.log
format: json

View File

@@ -0,0 +1,3 @@
DOMAIN=${DOMAIN}
WALLABAG_DB_PASSWORD=${WALLABAG_DB_PASSWORD}
WALLABAG_DB_ROOT_PASSWORD=${WALLABAG_DB_ROOT_PASSWORD}

View File

@@ -0,0 +1,55 @@
services:
wallabag:
image: wallabag/wallabag:latest
container_name: wallabag
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${WALLABAG_DB_ROOT_PASSWORD}
- SYMFONY__ENV__DATABASE_DRIVER=pdo_mysql
- SYMFONY__ENV__DATABASE_HOST=db
- SYMFONY__ENV__DATABASE_PORT=3306
- SYMFONY__ENV__DATABASE_NAME=wallabag
- SYMFONY__ENV__DATABASE_USER=wallabag
- SYMFONY__ENV__DATABASE_PASSWORD=${WALLABAG_DB_PASSWORD}
- SYMFONY__ENV__DATABASE_CHARSET=utf8mb4
- SYMFONY__ENV__MAILER_HOST=127.0.0.1
- SYMFONY__ENV__MAILER_USER=~
- SYMFONY__ENV__MAILER_PASSWORD=~
- SYMFONY__ENV__FROM_EMAIL=wallabag@${DOMAIN}
- SYMFONY__ENV__DOMAIN_NAME=https://wallabag.${DOMAIN}
expose:
- 80
networks:
- web
- default
volumes:
- ./images:/var/www/wallabag/web/assets/images
labels:
- "traefik.enable=true"
- "traefik.http.routers.wallabag.entrypoints=https"
- "traefik.http.routers.wallabag.rule=Host(`wallabag.${DOMAIN}`)"
- "traefik.http.routers.wallabag.tls.certresolver=http"
- "traefik.http.middlewares.wallabag-header.headers.customRequestHeaders.X-Forwarded-Proto=https"
- "traefik.http.routers.wallabag.middlewares=wallabag-header"
depends_on:
- db
- redis
db:
image: mariadb
container_name: wallabag-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${WALLABAG_DB_ROOT_PASSWORD}
- MARIADB_AUTO_UPGRADE=1
volumes:
- ./data:/var/lib/mysql
redis:
image: redis:alpine
container_name: wallabag-redis
restart: unless-stopped
networks:
web:
external: true

View File

@@ -0,0 +1,14 @@
services:
watchtower:
image: containrrr/watchtower:latest
container_name: watchtower
restart: unless-stopped
environment:
- TZ=America/New_York
- DOCKER_API_VERSION=1.44
- WATCHTOWER_SCHEDULE=0 0 4 * * *
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_INCLUDE_STOPPED=false
- WATCHTOWER_REVIVE_STOPPED=false
volumes:
- /var/run/docker.sock:/var/run/docker.sock

View File

@@ -0,0 +1 @@
DOMAIN=${DOMAIN}

View File

@@ -0,0 +1,26 @@
services:
xbackbone:
image: lscr.io/linuxserver/xbackbone:latest
container_name: xbackbone
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- ./config:/config
expose:
- 443
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.xbackbone.entrypoints=https"
- "traefik.http.routers.xbackbone.rule=Host(`xb.${DOMAIN}`)"
- "traefik.http.routers.xbackbone.tls.certresolver=http"
- "traefik.http.middlewares.xbackbone-header.headers.customRequestHeaders.X-Forwarded-Proto=https"
- "traefik.http.routers.xbackbone.middlewares=xbackbone-header"
networks:
web:
external: true

View File

@@ -0,0 +1,2 @@
DOMAIN=${DOMAIN}
# Copy additional env vars from /var/core/zerotier/denv on old prod

View File

@@ -0,0 +1,24 @@
services:
ztncui:
image: keynetworks/ztncui
container_name: zerotier-ui
restart: always
expose:
- 3180
volumes:
- ./ztncui:/opt/key-networks/ztncui/etc
- ./zt1:/var/lib/zerotier-one
env_file:
- .env
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.zerotier.entrypoints=https"
- "traefik.http.routers.zerotier.rule=Host(`zerotierui.${DOMAIN}`)"
- "traefik.http.routers.zerotier.tls.certresolver=http"
- "traefik.http.routers.zerotier.middlewares=dashboard-auth@file"
networks:
web:
external: true