While I try to spend a majority of my freetime outdoors, or at least interacting with nature in the form of working on my terrariums and aquariums, occasionally I have to indulge in some infrastructure bikeshedding and reimplementation of existing tools, like any other IT worker.
To that end, I run an increasingly-convoluted homelab setup, though I use the term loosely as it spans both my home and various baremetal server providers (thanks, Hetzner and OVH).
The software itself is a mix of tools from the wonderful Awesome-Selfhosted list and some questionable Go and Bash of my own design.
In front of it all sits Traefik, handling TLS termination, reverse proxying, and authentication (when I can be bothered).
I’ve included the full Docker Compose stack (yes, I’m aware of k8s, don’t @ me) used for this at the end of this post.
[…]
1## Please email [email protected] with any questions about this file, or setting up any services therein
2## The actual files are not a monolith like this one; this page is generated as a convenient reference
3
4## The following variables should be set in a file named .env in the same directory as docker-compose.yml (alter as needed):
5# - CLOUDFLARE_API_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
6# - TIMEZONE=America/Chicago
7# - UID=1000
8# - GID=1000
9
10services:
11
12 ### https://github.com/traefik/traefik
13 ## Files for basicauth middleware should be generated with the following:
14 # $ htpasswd -c "${filename}" "${username}"
15 traefik:
16 image: traefik:latest
17 container_name: traefik
18 restart: unless-stopped
19 environment:
20 - "CF_DNS_API_TOKEN=${CLOUDFLARE_API_TOKEN}"
21 - "TZ=${TIMEZONE:?not set}"
22 command:
23 - "--accesslog=true"
24 - "--accesslog.bufferingsize=100"
25 - "--accesslog.fields.names.StartUTC=drop"
26 - "--accesslog.filepath=/var/log/access.log"
27 - "--api"
28 - "--certificatesresolvers.letsencrypt.acme.dnschallenge=true"
29 - "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"
30 - "--certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53"
31 - "--certificatesresolvers.letsencrypt.acme.storage=/certs/acme.json"
32 - "--entrypoints.http-private.address=:1080"
33 - "--entrypoints.http-private.http.redirections.entrypoint.scheme=https"
34 - "--entrypoints.http-private.http.redirections.entryPoint.to=https-private"
35 - "--entrypoints.https-private.address=:1443"
36 - "--entrypoints.https-private.http3"
37 - "--entrypoints.https-private.http3.advertisedport=443"
38 - "--entrypoints.http-public.address=:2080"
39 - "--entrypoints.http-public.http.redirections.entrypoint.scheme=https"
40 - "--entrypoints.http-public.http.redirections.entryPoint.to=https-public"
41 - "--entrypoints.https-public.address=:2443"
42 - "--entrypoints.https-public.http3"
43 - "--entrypoints.https-public.http3.advertisedport=443"
44 - "--log.filePath=/var/log/traefik.log"
45 - "--log.level=ERROR"
46 - "--providers.docker=true"
47 - "--providers.docker.exposedByDefault=false"
48 - "--providers.docker.network=traefik"
49 - "--providers.file.directory=/conf"
50 - "--providers.file.watch=true"
51 - "--providers.providersThrottleDuration=10s"
52 - "--serversTransport.insecureSkipVerify=true"
53 labels:
54 - "traefik.enable=true"
55 - "traefik.http.routers.traefik.rule=Host(`dashboard.seedno.de`)"
56 - "traefik.http.routers.traefik.entrypoints=https-public"
57 - "traefik.http.routers.traefik.service=api@internal"
58 - "traefik.http.routers.traefik.tls=true"
59 - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
60 - "traefik.http.routers.traefik.middlewares=adminauth@file,compress@file,errors@file,secure@file"
61 ports:
62 - "${TAILSCALE_IP:?not set}:80:1080"
63 - "${TAILSCALE_IP:?not set}:443:1443"
64 - "${TAILSCALE_IP:?not set}:443:1443/udp"
65 - "${PUBLIC_IP:?not set}:80:2080"
66 - "${PUBLIC_IP:?not set}:443:2443"
67 - "${PUBLIC_IP:?not set}:443:2443/udp"
68 networks:
69 - traefik
70 - audio
71 - code-server
72 - cyberchef
73 - errors
74 - feedcord
75 - guacamole
76 - huginn
77 - naughty
78 - nginx
79 - ollama
80 - open-webui
81 - query
82 - random
83 - recipya
84 - registry
85 - stirling-pdf
86 - thelounge
87 - trivia
88 - vaultwarden
89 - wastebin
90 volumes:
91 - type: bind
92 source: /docker/traefik/certs
93 target: /certs
94 - type: bind
95 source: /docker/traefik/config
96 target: /conf
97 read_only: true
98 - type: bind
99 source: /docker/traefik/logs
100 target: /var/log
101 - type: bind
102 source: /var/run/docker.sock
103 target: /var/run/docker.sock
104 read_only: true
105
106 ### https://github.com/Seednode/roulette
107 audio:
108 image: oci.seedno.de/seednode/roulette:latest
109 container_name: audio
110 restart: unless-stopped
111 environment:
112 - "ROULETTE_AUDIO=true"
113 - "ROULETTE_DEBUG=true"
114 - "ROULETTE_PREFIX=/random/"
115 - "ROULETTE_RECURSIVE=true"
116 - "ROULETTE_VERBOSE=true"
117 - "TZ=${TIMEZONE:?not set}"
118 command:
119 - "/data"
120 labels:
121 - "traefik.enable=true"
122 - "traefik.http.middlewares.audio.headers.contentSecurityPolicy=default-src 'self'"
123 - "traefik.http.routers.audio.rule=Host(`voice.seedno.de`) && PathPrefix(`/random`)"
124 - "traefik.http.routers.audio.entrypoints=https-public"
125 - "traefik.http.routers.audio.service=audio"
126 - "traefik.http.routers.audio.tls=true"
127 - "traefik.http.routers.audio.tls.certresolver=letsencrypt"
128 - "traefik.http.routers.audio.middlewares=audio,compress@file,errors@file,secure@file"
129 - "traefik.http.services.audio.loadbalancer.server.port=8080"
130 networks:
131 - audio
132 volumes:
133 - type: bind
134 source: /var/www/html/voice.seedno.de
135 target: /data
136 read_only: true
137
138 ### https://github.com/coder/code-server
139 code-server:
140 image: lscr.io/linuxserver/code-server:latest
141 container_name: code-server
142 restart: unless-stopped
143 privileged: true
144 environment:
145 PUID: ${UID:?not set}
146 PGID: ${GID:?not set}
147 SUDO_PASSWORD: ${CODE_SUDO_PASSWORD:?not set}
148 TZ: ${TIMEZONE:?not set}
149 INSTALL_PACKAGES: "fsharp rust-src"
150 DOCKER_MODS: "linuxserver/mods:code-server-awscli|\
151 linuxserver/mods:code-server-dotnet|\
152 linuxserver/mods:code-server-extension-arguments|\
153 linuxserver/mods:code-server-golang|\
154 linuxserver/mods:code-server-java11|\
155 linuxserver/mods:code-server-nodejs|\
156 linuxserver/mods:code-server-php8|\
157 linuxserver/mods:code-server-powershell|\
158 linuxserver/mods:code-server-python3|\
159 linuxserver/mods:code-server-rust|\
160 linuxserver/mods:code-server-scikit-learn|\
161 linuxserver/mods:code-server-shellcheck|\
162 linuxserver/mods:code-server-terraform|\
163 linuxserver/mods:code-server-zsh|\
164 linuxserver/mods:universal-package-install"
165 VSCODE_EXTENSION_IDS: "amodio.tsl-problem-matcher|\
166 bungcip.better-toml|\
167 coolbear.systemd-unit-file|\
168 dbaeumer.vscode-eslint|\
169 esbenp.prettier-vscode|\
170 golang.go|\
171 Ionide.Ionide-fsharp|\
172 mads-hartmann.bash-ide-vscode|\
173 ms-azuretools.vscode-containers|\
174 ms-dotnettools.csdevkit|\
175 ms-python.python|\
176 ms-vscode.PowerShell|\
177 muhammad-sammy.csharp|\
178 rust-lang.rust-analyzer|\
179 timonwong.shellcheck"
180 labels:
181 - "traefik.enable=true"
182 - "traefik.http.middlewares.code-server.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline' https://*.vscode-cdn.net; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.vscode-cdn.net; img-src 'self' data: https://*.vscode-cdn.net; connect-src 'self' https://open-vsx.org"
183 - "traefik.http.routers.code-server.rule=Host(`code.seedno.de`)"
184 - "traefik.http.routers.code-server.entrypoints=https-public"
185 - "traefik.http.routers.code-server.service=code-server"
186 - "traefik.http.routers.code-server.tls=true"
187 - "traefik.http.routers.code-server.tls.certresolver=letsencrypt"
188 - "traefik.http.routers.code-server.middlewares=adminauth@file,code-server,compress@file,errors@file,secure@file"
189 - "traefik.http.services.code-server.loadbalancer.server.port=8443"
190 networks:
191 - code-server
192 volumes:
193 - type: bind
194 source: /docker/code-server/config
195 target: /config
196 - type: bind
197 source: /home/sinc/bin
198 target: /config/workspace/bin
199 - type: bind
200 source: /home/sinc/code
201 target: /config/workspace/code
202 - type: bind
203 source: /home/sinc/Dropbox/Blog
204 target: /config/workspace/blog
205 - type: bind
206 source: /home/sinc/Dropbox/Documents
207 target: /config/workspace/documents
208 - type: bind
209 source: /home/sinc/Dropbox/KB
210 target: /config/workspace/kb
211 - type: bind
212 source: /home/sinc/trivia
213 target: /config/workspace/trivia
214 - type: bind
215 source: /var/www/html
216 target: /config/workspace/html
217
218 ### https://github.com/gchq/CyberChef
219 cyberchef:
220 image: ghcr.io/gchq/cyberchef:latest
221 container_name: cyberchef
222 restart: unless-stopped
223 labels:
224 - "traefik.enable=true"
225 - "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:"
226 - "traefik.http.routers.cyberchef.rule=Host(`tools.seedno.de`)"
227 - "traefik.http.routers.cyberchef.entrypoints=https-public"
228 - "traefik.http.routers.cyberchef.service=cyberchef"
229 - "traefik.http.routers.cyberchef.tls=true"
230 - "traefik.http.routers.cyberchef.tls.certresolver=letsencrypt"
231 - "traefik.http.routers.cyberchef.middlewares=compress@file,cyberchef,errors@file,secure@file"
232 - "traefik.http.services.cyberchef.loadbalancer.server.port=80"
233 networks:
234 - cyberchef
235
236 ### https://git.seedno.de/seednode/docker-nginx
237 errors:
238 image: oci.seedno.de/seednode/nginx:latest
239 container_name: errors
240 restart: unless-stopped
241 labels:
242 - "traefik.enable=true"
243 - "traefik.http.routers.errors.entrypoints=https-public"
244 - "traefik.http.services.errors.loadbalancer.server.port=8080"
245 networks:
246 - errors
247 volumes:
248 - type: bind
249 source: /docker/errors/config/nginx.conf
250 target: /etc/nginx/nginx.conf
251 read_only: true
252 - type: bind
253 source: /docker/errors/logs
254 target: /var/log/nginx
255 - type: bind
256 source: /docker/errors/data
257 target: /var/www/html
258 read_only: true
259
260 ### https://github.com/Qolors/FeedCord
261 feedcord:
262 image: qolors/feedcord:latest
263 container_name: feedcord
264 restart: unless-stopped
265 networks:
266 - feedcord
267 volumes:
268 - type: bind
269 source: /docker/feedcord/data/appsettings.json
270 target: /app/config/appsettings.json
271 read_only: true
272
273 ### https://github.com/apache/guacamole-server
274 ## To generate the database schema:
275 # $ docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --postgresql > /tmp/initdb.sql
276 # $ docker cp /tmp/initdb.sql guacamole-db:/tmp/initdb.sql
277 # $ docker exec -it guacamole-db /bin/sh -c "cat /tmp/initdb.sql | psql --dbname=${GUACAMOLE_DATABASE_NAME} --user=${GUACAMOLE_DATABASE_USER} -f -"
278 ## When configuring a connection, set the following for guacd parameters:
279 # - Hostname: guacd
280 # - Port: 4822
281 # - Encryption: None (unencrypted)
282 ## RDP sessions should have a security mode of "NLA (Network Level Authentication)"
283 guacamole:
284 image: guacamole/guacamole:latest
285 container_name: guacamole
286 restart: unless-stopped
287 environment:
288 - "GUACD_HOSTNAME=guacd"
289 - "POSTGRES_HOSTNAME=guacamole-db"
290 - "POSTGRES_DATABASE=${GUACAMOLE_DATABASE_NAME:?not set}"
291 - "POSTGRES_USER=${GUACAMOLE_DATABASE_USER:?not set}"
292 - "POSTGRES_PASSWORD=${GUACAMOLE_DATABASE_PASS:?not set}"
293 labels:
294 - "traefik.enable=true"
295 - "traefik.http.middlewares.guacamole.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; img-src 'self' data:"
296 - "traefik.http.routers.guacamole.rule=Host(`guacamole.seedno.de`) && PathPrefix(`/guacamole/`)"
297 - "traefik.http.routers.guacamole.entrypoints=https-public"
298 - "traefik.http.routers.guacamole.service=guacamole"
299 - "traefik.http.routers.guacamole.tls=true"
300 - "traefik.http.routers.guacamole.tls.certresolver=letsencrypt"
301 - "traefik.http.routers.guacamole.middlewares=compress@file,errors@file,secure@file,guacamole"
302 - "traefik.http.services.guacamole.loadbalancer.server.port=8080"
303 networks:
304 - guacamole
305
306 guacamole-db:
307 image: postgres:16-alpine
308 container_name: guacamole-db
309 restart: unless-stopped
310 environment:
311 - "POSTGRES_DB=${GUACAMOLE_DATABASE_NAME:?not set}"
312 - "POSTGRES_USER=${GUACAMOLE_DATABASE_USER:?not set}"
313 - "POSTGRES_PASSWORD=${GUACAMOLE_DATABASE_PASS:?not set}"
314 networks:
315 - guacamole
316 volumes:
317 - type: bind
318 source: /docker/guacamole/database
319 target: /var/lib/postgresql/data
320
321 guacd:
322 image: guacamole/guacd
323 container_name: guacd
324 restart: unless-stopped
325 networks:
326 - guacamole
327
328 ### https://github.com/huginn/huginn
329 huginn-web:
330 image: ghcr.io/huginn/huginn-single-process:latest
331 container_name: huginn-web
332 restart: unless-stopped
333 depends_on:
334 - huginn-db
335 environment:
336 - "APP_SECRET_TOKEN=${HUGINN_APP_SECRET:?not set}"
337 - "DATABASE_ADAPTER=postgresql"
338 - "DATABASE_ENCODING=utf8"
339 - "DATABASE_NAME=${HUGINN_DATABASE_NAME:?not set}"
340 - "DATABASE_USERNAME=${HUGINN_DATABASE_USER:?not set}"
341 - "DATABASE_PASSWORD=${HUGINN_DATABASE_PASS:?not set}"
342 - "DATABASE_POOL=20"
343 - "DATABASE_RECONNECT=true"
344 - "DOMAIN=odin.seedno.de:443"
345 - "FORCE_SSL=true"
346 - "INVITATION_CODE=${HUGINN_INVITE_CODE:?not set}"
347 - "PORT=3000"
348 - "POSTGRES_PORT_5432_TCP_ADDR=huginn-db"
349 - "POSTGRES_PORT_5432_TCP_PORT=5432"
350 - "RAILS_ENV=production"
351 - "REQUIRE_CONFIRMED_EMAIL=false"
352 - "SKIP_INVITATION_CODE=false"
353 - "TIMEZONE=${TIMEZONE:?not set}"
354 labels:
355 - "traefik.enable=true"
356 - "traefik.http.routers.huginn.rule=Host(`odin.seedno.de`)"
357 - "traefik.http.routers.huginn.entrypoints=https-public"
358 - "traefik.http.routers.huginn.service=huginn"
359 - "traefik.http.routers.huginn.tls=true"
360 - "traefik.http.routers.huginn.tls.certresolver=letsencrypt"
361 - "traefik.http.routers.huginn.middlewares=compress@file,errors@file,secure@file"
362 - "traefik.http.services.huginn.loadbalancer.server.port=3000"
363 networks:
364 - huginn
365 - huginn-db
366
367 huginn-threaded:
368 image: ghcr.io/huginn/huginn-single-process:latest
369 container_name: huginn-threaded
370 restart: unless-stopped
371 depends_on:
372 - huginn-db
373 - huginn-web
374 environment:
375 - "APP_SECRET_TOKEN=${HUGINN_APP_SECRET:?not set}"
376 - "DATABASE_ADAPTER=postgresql"
377 - "DATABASE_ENCODING=utf8"
378 - "DATABASE_NAME=${HUGINN_DATABASE_NAME:?not set}"
379 - "DATABASE_USERNAME=${HUGINN_DATABASE_USER:?not set}"
380 - "DATABASE_PASSWORD=${HUGINN_DATABASE_PASS:?not set}"
381 - "DATABASE_POOL=20"
382 - "DATABASE_RECONNECT=true"
383 - "DOMAIN=odin.seedno.de:443"
384 - "FORCE_SSL=true"
385 - "INVITATION_CODE=${HUGINN_INVITE_CODE:?not set}"
386 - "PORT=3000"
387 - "POSTGRES_PORT_5432_TCP_ADDR=huginn-db"
388 - "POSTGRES_PORT_5432_TCP_PORT=5432"
389 - "RAILS_ENV=production"
390 - "REQUIRE_CONFIRMED_EMAIL=false"
391 - "SKIP_INVITATION_CODE=false"
392 - "TIMEZONE=${TIMEZONE:?not set}"
393 command: /scripts/init bin/threaded.rb
394 networks:
395 - huginn
396 - huginn-db
397
398 ### https://github.com/postgres/postgres
399 huginn-db:
400 image: postgres:16-alpine
401 container_name: huginn-db
402 restart: unless-stopped
403 environment:
404 - "POSTGRES_DB=${HUGINN_DATABASE_NAME:?not set}"
405 - "POSTGRES_USER=${HUGINN_DATABASE_USER:?not set}"
406 - "POSTGRES_PASSWORD=${HUGINN_DATABASE_PASS:?not set}"
407 networks:
408 - huginn-db
409 volumes:
410 - type: bind
411 source: /docker/huginn/database
412 target: /var/lib/postgresql/data
413
414 ### https://github.com/postgres/postgres
415 learning-db:
416 image: postgres:16-alpine
417 container_name: learning-db
418 restart: unless-stopped
419 environment:
420 - "POSTGRES_DB=${LEARNING_DATABASE_NAME:?not set}"
421 - "POSTGRES_USER=${LEARNING_DATABASE_USER:?not set}"
422 - "POSTGRES_PASSWORD=${LEARNING_DATABASE_PASS:?not set}"
423 networks:
424 - learning-db
425 volumes:
426 - type: bind
427 source: /docker/learning/database
428 target: /var/lib/postgresql/data
429
430 ### https://github.com/Seednode/docker-nginx
431 naughty:
432 image: oci.seedno.de/seednode/nginx:latest
433 container_name: naughty
434 restart: unless-stopped
435 labels:
436 - "traefik.enable=true"
437 - "traefik.http.routers.naughty.rule=PathPrefix(`/{path:.*(\\.env|\\.aws|xmlrpc|wp-admin|wp-login).*}`)"
438 - "traefik.http.routers.naughty.priority=25400"
439 - "traefik.http.routers.naughty.entrypoints=https-public"
440 - "traefik.http.routers.naughty.service=naughty"
441 - "traefik.http.routers.naughty.tls=true"
442 - "traefik.http.routers.naughty.tls.certresolver=letsencrypt"
443 - "traefik.http.routers.naughty.middlewares=compress@file,errors@file,secure@file"
444 - "traefik.http.services.naughty.loadbalancer.server.port=8080"
445 networks:
446 - naughty
447 volumes:
448 - type: bind
449 source: /docker/naughty/config/nginx.conf
450 target: /etc/nginx/nginx.conf
451 read_only: true
452 - type: bind
453 source: /docker/naughty/logs
454 target: /var/log/nginx
455 - type: bind
456 source: /docker/naughty/data
457 target: /var/www/html
458 read_only: true
459
460 ### https://github.com/Seednode/docker-nginx
461 ## All my sites are configured via the file provider
462 ## An example is available here:
463 ## https://cdn.seedno.de/txt/access.seedno.de.yaml
464 nginx:
465 image: oci.seedno.de/seednode/nginx:latest
466 container_name: nginx
467 restart: unless-stopped
468 depends_on:
469 - nginx-php-fpm
470 networks:
471 - nginx
472 - nginx-php
473 volumes:
474 - type: bind
475 source: /docker/nginx/config
476 target: /etc/nginx
477 read_only: true
478 - type: bind
479 source: /docker/nginx/logs
480 target: /var/log/nginx
481 - type: bind
482 source: /var/www/html
483 target: /var/www/html
484 read_only: true
485
486 nginx-php-fpm:
487 image: php:8-fpm-alpine
488 container_name: nginx-php-fpm
489 restart: unless-stopped
490 networks:
491 - nginx-php
492 volumes:
493 - type: bind
494 source: /var/www/html
495 target: /var/www/html
496 read_only: true
497
498 ### https://github.com/ollama/ollama
499 ollama:
500 image: ollama/ollama:latest
501 container_name: ollama
502 restart: unless-stopped
503 labels:
504 - "traefik.enable=true"
505 - "traefik.http.routers.ollama.rule=Host(`ollama.seedno.de`)"
506 - "traefik.http.routers.ollama.entrypoints=https-private"
507 - "traefik.http.routers.ollama.service=ollama"
508 - "traefik.http.routers.ollama.tls=true"
509 - "traefik.http.routers.ollama.tls.certresolver=letsencrypt"
510 - "traefik.http.routers.ollama.middlewares=allowlist@file,compress@file,errors@file,secure@file"
511 - "traefik.http.services.ollama.loadbalancer.server.port=11434"
512 networks:
513 - ollama
514 volumes:
515 - type: bind
516 source: /docker/ollama/data
517 target: /root/.ollama
518
519 ### https://github.com/open-webui/open-webui
520 open-webui:
521 image: ghcr.io/open-webui/open-webui:main
522 container_name: open-webui
523 restart: unless-stopped
524 environment:
525 - "[email protected]"
526 - "CORS_ALLOW_ORIGIN=https://*.seedno.de"
527 - "ENABLE_FORWARD_USER_INFO_HEADERS=True"
528 - "ENABLE_OPENAI_API=False"
529 - "ENABLE_PERSISTENT_CONFIG=False"
530 - "ENABLE_SIGNUP=False"
531 - "OFFLINE_MODE=True"
532 - "OLLAMA_BASE_URL=http://ollama:11434"
533 - "WEBUI_AUTH=False"
534 - "WEBUI_NAME=Seedno.deAI"
535 - "WEBUI_URL=https://ai.seedno.de"
536 labels:
537 - "traefik.enable=true"
538 - "traefik.http.routers.open-webui.rule=Host(`ai.seedno.de`)"
539 - "traefik.http.routers.open-webui.entrypoints=https-private"
540 - "traefik.http.routers.open-webui.service=open-webui"
541 - "traefik.http.routers.open-webui.tls=true"
542 - "traefik.http.routers.open-webui.tls.certresolver=letsencrypt"
543 - "traefik.http.routers.open-webui.middlewares=allowlist@file,compress@file,errors@file,secure@file"
544 - "traefik.http.services.open-webui.loadbalancer.server.port=8080"
545 networks:
546 - open-webui
547 - ollama
548 volumes:
549 - type: bind
550 source: /docker/open-webui/data
551 target: /app/backend/data
552
553 ### https://github.com/Seednode/query
554 query:
555 image: oci.seedno.de/seednode/query:latest
556 container_name: query
557 restart: unless-stopped
558 environment:
559 - "QUERY_ALL=true"
560 - "QUERY_DNS_RESOLVER=1.1.1.1:53"
561 - "QUERY_MAX_DICE_ROLLS=1024"
562 - "QUERY_MAX_DICE_SIDES=1024"
563 - "QUERY_QR_SIZE=512"
564 - "QUERY_VERBOSE=true"
565 - "TZ=${TIMEZONE:?not set}"
566 labels:
567 - "traefik.enable=true"
568 - "traefik.http.middlewares.query.headers.contentSecurityPolicy=default-src 'self'"
569 - "traefik.http.routers.query.rule=Host(`q.seedno.de`)"
570 - "traefik.http.routers.query.entrypoints=https-public"
571 - "traefik.http.routers.query.service=query"
572 - "traefik.http.routers.query.tls=true"
573 - "traefik.http.routers.query.tls.certresolver=letsencrypt"
574 - "traefik.http.routers.query.middlewares=compress@file,errors@file,query,ratelimit@file,secure@file"
575 - "traefik.http.services.query.loadbalancer.server.port=8080"
576 networks:
577 - query
578
579 ### https://github.com/Seednode/roulette
580 random:
581 image: oci.seedno.de/seednode/roulette:latest
582 container_name: random
583 restart: unless-stopped
584 environment:
585 - "ROULETTE_ALL=true"
586 - "ROULETTE_DEBUG=true"
587 - "ROULETTE_PREFIX=/random/"
588 - "ROULETTE_RECURSIVE=true"
589 - "ROULETTE_VERBOSE=true"
590 - "TZ=${TIMEZONE:?not set}"
591 command:
592 - "/data"
593 labels:
594 - "traefik.enable=true"
595 - "traefik.http.middlewares.random.headers.contentSecurityPolicy=default-src 'self'"
596 - "traefik.http.routers.random.rule=Host(`cdn.seedno.de`) && PathPrefix(`/random`)"
597 - "traefik.http.routers.random.entrypoints=https-public"
598 - "traefik.http.routers.random.service=random"
599 - "traefik.http.routers.random.tls=true"
600 - "traefik.http.routers.random.tls.certresolver=letsencrypt"
601 - "traefik.http.routers.random.middlewares=compress@file,errors@file,random,secure@file"
602 - "traefik.http.services.random.loadbalancer.server.port=8080"
603 networks:
604 - random
605 volumes:
606 - type: bind
607 source: /var/www/html/cdn.seedno.de
608 target: /data
609 read_only: true
610
611 ### https://github.com/reaper47/recipya
612 recipya:
613 image: reaper99/recipya:nightly
614 container_name: recipya
615 restart: unless-stopped
616 environment:
617 - "RECIPYA_SERVER_IS_PROD=true"
618 - "RECIPYA_SERVER_NO_SIGNUPS=true"
619 - "RECIPYA_SERVER_PORT=8078"
620 - "RECIPYA_SERVER_URL=https://recipes.seedno.de"
621 labels:
622 - "traefik.enable=true"
623 - "traefik.http.routers.recipya.rule=Host(`recipes.seedno.de`)"
624 - "traefik.http.routers.recipya.entrypoints=https-public"
625 - "traefik.http.routers.recipya.service=recipya"
626 - "traefik.http.routers.recipya.tls=true"
627 - "traefik.http.routers.recipya.tls.certresolver=letsencrypt"
628 - "traefik.http.routers.recipya.middlewares=compress@file,errors@file,secure@file"
629 - "traefik.http.services.recipya.loadbalancer.server.port=8078"
630 networks:
631 - recipya
632 volumes:
633 - type: bind
634 source: /docker/recipya/data
635 target: /home/recipya/.config/Recipya
636
637 ### https://hub.docker.com/_/registry
638 registry:
639 image: registry:3
640 container_name: registry
641 restart: unless-stopped
642 environment:
643 - "REGISTRY_HTTP_ADDR=0.0.0.0:5000"
644 - "REGISTRY_HTTP_SECRET=${REGISTRY_SECRET:?not set}"
645 labels:
646 - "traefik.enable=true"
647 - "traefik.http.routers.registry.rule=Host(`oci.seedno.de`)"
648 - "traefik.http.routers.registry.entrypoints=https-private"
649 - "traefik.http.routers.registry.service=registry"
650 - "traefik.http.routers.registry.tls=true"
651 - "traefik.http.routers.registry.tls.certresolver=letsencrypt"
652 - "traefik.http.routers.registry.middlewares=allowlist@file,compress@file,errors@file,secure@file"
653 - "traefik.http.services.registry.loadbalancer.server.port=5000"
654 networks:
655 - registry
656 volumes:
657 - type: bind
658 source: /docker/registry/config/config.yml
659 target: /etc/docker/registry/config.yml
660 read_only: true
661 - type: bind
662 source: /docker/registry/data
663 target: /var/lib/registry
664
665 ### https://github.com/Stirling-Tools/Stirling-PDF
666 stirling-pdf:
667 image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest
668 container_name: stirling-pdf
669 restart: unless-stopped
670 environment:
671 - "DOCKER_ENABLE_SECURITY=false"
672 - "LANGS=en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID"
673 - "METRICS_ENABLED=false"
674 - "SECURITY_ENABLELOGIN=false"
675 - "SHOW_SURVEY=false"
676 - "SYSTEM_DEFAULTLOCALE=en-US"
677 - "SYSTEM_GOOGLEVISIBILITY=false"
678 - "SYSTEM_MAXFILESIZE=100"
679 - "UI_APPNAME=PDF Converter"
680 - "UI_APPNAMENAVBAR=PDF Converter"
681 - "UI_HOMEDESCRIPTION=Seednode's PDF Converter"
682 labels:
683 - "traefik.enable=true"
684 - "traefik.http.routers.stirling-pdf.rule=Host(`pdf.seedno.de`)"
685 - "traefik.http.routers.stirling-pdf.entrypoints=https-public"
686 - "traefik.http.routers.stirling-pdf.service=stirling-pdf"
687 - "traefik.http.routers.stirling-pdf.tls=true"
688 - "traefik.http.routers.stirling-pdf.tls.certresolver=letsencrypt"
689 - "traefik.http.routers.stirling-pdf.middlewares=adminauth@file,compress@file,secure@file"
690 - "traefik.http.services.stirling-pdf.loadbalancer.server.port=8080"
691 networks:
692 - stirling-pdf
693 volumes:
694 - type: bind
695 source: /docker/stirling-pdf/config
696 target: /configs
697 - type: bind
698 source: /docker/stirling-pdf/logs
699 target: /logs
700 - type: bind
701 source: /docker/stirling-pdf/data
702 target: /docker/tessdata
703
704 ### https://github.com/thelounge/thelounge
705 thelounge:
706 image: lscr.io/linuxserver/thelounge:latest
707 container_name: thelounge
708 restart: unless-stopped
709 environment:
710 - "PUID=${UID:?not set}"
711 - "PGID=${GID:?not set}"
712 - "TZ=${TIMEZONE:?not set}"
713 labels:
714 - "traefik.enable=true"
715 - "traefik.http.middlewares.thelounge.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'"
716 - "traefik.http.routers.thelounge.rule=Host(`chat.seedno.de`)"
717 - "traefik.http.routers.thelounge.entrypoints=https-public"
718 - "traefik.http.routers.thelounge.service=thelounge"
719 - "traefik.http.routers.thelounge.tls=true"
720 - "traefik.http.routers.thelounge.tls.certresolver=letsencrypt"
721 - "traefik.http.routers.thelounge.middlewares=compress@file,errors@file,secure@file,thelounge"
722 - "traefik.http.services.thelounge.loadbalancer.server.port=9000"
723 networks:
724 - thelounge
725 volumes:
726 - type: bind
727 source: /docker/thelounge/config
728 target: /config
729
730 ### https://github.com/Seednode/trivia
731 trivia:
732 image: oci.seedno.de/seednode/trivia:latest
733 container_name: trivia
734 restart: unless-stopped
735 environment:
736 - "TRIVIA_COLORS=/data/colors.txt"
737 - "TRIVIA_HTML=true"
738 - "TRIVIA_RECURSIVE=true"
739 - "TRIVIA_RELOAD=true"
740 - "TRIVIA_RELOAD_INTERVAL=30m"
741 - "TRIVIA_VERBOSE=true"
742 - "TZ=${TIMEZONE:?not set}"
743 command:
744 - "/data/trivial-pursuit"
745 labels:
746 - "traefik.enable=true"
747 - "traefik.mode=public"
748 - "traefik.http.routers.trivia.rule=Host(`trivia.seedno.de`)"
749 - "traefik.http.routers.trivia.entrypoints=https-public"
750 - "traefik.http.routers.trivia.service=trivia"
751 - "traefik.http.routers.trivia.tls=true"
752 - "traefik.http.routers.trivia.tls.certresolver=letsencrypt"
753 - "traefik.http.routers.trivia.middlewares=compress@file,errors@file,secure@file"
754 - "traefik.http.services.trivia.loadbalancer.server.port=8080"
755 networks:
756 - trivia
757 volumes:
758 - type: bind
759 source: /home/sinc/trivia
760 target: /data
761 read_only: true
762
763 ### https://github.com/dani-garcia/vaultwarden
764 vaultwarden:
765 image: vaultwarden/server:alpine
766 container_name: vaultwarden
767 restart: unless-stopped
768 depends_on:
769 - vaultwarden-db
770 environment:
771 - "DATABASE_URL=postgresql://${VAULTWARDEN_DATABASE_USER:?not set}:${VAULTWARDEN_DATABASE_PASS:?not set}@vaultwarden-db:5432/${VAULTWARDEN_DATABASE_NAME:?not set}"
772 - "DOMAIN=https://vault.seedno.de"
773 - "WEBSOCKET_ENABLED=false"
774 - "SIGNUPS_ALLOWED=true"
775 - "INVITATIONS_ALLOWED=false"
776 - "SHOW_PASSWORD_HINT=false"
777 - "TRASH_AUTO_DELETE_DAYS=30"
778 - "LOG_FILE=/data/vaultwarden.log"
779 - "ROCKET_PORT=80"
780 labels:
781 - "traefik.enable=true"
782 - "traefik.http.middlewares.vaultwarden.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; img-src 'self' data:"
783 - "traefik.http.routers.vaultwarden.rule=Host(`vault.seedno.de`)"
784 - "traefik.http.routers.vaultwarden.entrypoints=https-private"
785 - "traefik.http.routers.vaultwarden.service=vaultwarden"
786 - "traefik.http.routers.vaultwarden.tls=true"
787 - "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
788 - "traefik.http.routers.vaultwarden.middlewares=compress@file,errors@file,secure@file,vaultwarden"
789 - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
790 networks:
791 - vaultwarden
792 - vaultwarden-db
793 volumes:
794 - type: bind
795 source: /docker/vaultwarden/data
796 target: /data
797
798 vaultwarden-db:
799 image: postgres:16-alpine
800 container_name: vaultwarden-db
801 restart: unless-stopped
802 environment:
803 - "POSTGRES_DB=${VAULTWARDEN_DATABASE_NAME:?not set}"
804 - "POSTGRES_USER=${VAULTWARDEN_DATABASE_USER:?not set}"
805 - "POSTGRES_PASSWORD=${VAULTWARDEN_DATABASE_PASS:?not set}"
806 networks:
807 - vaultwarden-db
808 volumes:
809 - type: bind
810 source: /docker/vaultwarden/database
811 target: /var/lib/postgresql/data
812
813 ### https://github.com/matze/wastebin
814 wastebin:
815 image: quxfoo/wastebin:latest
816 container_name: wastebin
817 restart: unless-stopped
818 environment:
819 - "WASTEBIN_BASE_URL=https://paste.seedno.de"
820 - "WASTEBIN_DATABASE_PATH=/data/state.db"
821 - "WASTEBIN_MAX_PASTE_EXPIRATION=2678400"
822 - "WASTEBIN_PASSWORD_SALT=${WASTEBIN_PASSWORD_SALT:?not set}"
823 - "WASTEBIN_SIGNING_KEY=${WASTEBIN_SIGNING_KEY:?not set}"
824 - "WASTEBIN_THEME=solarized"
825 - "WASTEBIN_TITLE=Pastebin"
826 labels:
827 - "traefik.enable=true"
828 - "traefik.http.middlewares.wastebin.headers.contentSecurityPolicy=default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; img-src 'self' data:"
829 - "traefik.http.routers.wastebin.rule=Host(`paste.seedno.de`)"
830 - "traefik.http.routers.wastebin.entrypoints=https-public"
831 - "traefik.http.routers.wastebin.service=wastebin"
832 - "traefik.http.routers.wastebin.tls=true"
833 - "traefik.http.routers.wastebin.tls.certresolver=letsencrypt"
834 - "traefik.http.routers.wastebin.middlewares=compress@file,errors@file,secure@file,wastebin"
835 - "traefik.http.services.wastebin.loadbalancer.server.port=8088"
836 networks:
837 - wastebin
838 volumes:
839 - type: bind
840 source: /docker/wastebin/data
841 target: /data
842
843networks:
844 traefik:
845 name: traefik
846 driver: bridge
847 driver_opts:
848 com.docker.network.bridge.name: traefik
849 audio:
850 name: audio
851 driver: bridge
852 driver_opts:
853 com.docker.network.bridge.name: audio
854 internal: true
855 code-server:
856 name: code-server
857 driver: bridge
858 driver_opts:
859 com.docker.network.bridge.name: code-server
860 cyberchef:
861 name: cyberchef
862 driver: bridge
863 driver_opts:
864 com.docker.network.bridge.name: cyberchef
865 internal: true
866 errors:
867 name: errors
868 driver: bridge
869 driver_opts:
870 com.docker.network.bridge.name: errors
871 internal: true
872 feedcord:
873 name: feedcord
874 driver: bridge
875 driver_opts:
876 com.docker.network.bridge.name: feedcord
877 guacamole:
878 name: guacamole
879 driver: bridge
880 driver_opts:
881 com.docker.network.bridge.name: guacamole
882 huginn:
883 name: huginn
884 driver: bridge
885 driver_opts:
886 com.docker.network.bridge.name: huginn
887 huginn-db:
888 name: huginn-db
889 driver: bridge
890 driver_opts:
891 com.docker.network.bridge.name: huginn-db
892 learning-db:
893 name: learning-db
894 driver: bridge
895 driver_opts:
896 com.docker.network.bridge.name: learning-db
897 internal: true
898 naughty:
899 name: naughty
900 driver: bridge
901 driver_opts:
902 com.docker.network.bridge.name: naughty
903 internal: true
904 nginx:
905 name: nginx
906 driver: bridge
907 driver_opts:
908 com.docker.network.bridge.name: nginx
909 nginx-php:
910 name: nginx-php
911 driver: bridge
912 driver_opts:
913 com.docker.network.bridge.name: nginx-php
914 internal: true
915 ollama:
916 name: ollama
917 driver: bridge
918 driver_opts:
919 com.docker.network.bridge.name: ollama
920 open-webui:
921 name: open-webui
922 driver: bridge
923 driver_opts:
924 com.docker.network.bridge.name: open-webui
925 query:
926 name: query
927 driver: bridge
928 driver_opts:
929 com.docker.network.bridge.name: query
930 random:
931 name: random
932 driver: bridge
933 driver_opts:
934 com.docker.network.bridge.name: random
935 internal: true
936 recipya:
937 name: recipya
938 driver: bridge
939 driver_opts:
940 com.docker.network.bridge.name: recipya
941 registry:
942 name: registry
943 driver: bridge
944 driver_opts:
945 com.docker.network.bridge.name: registry
946 internal: true
947 stirling-pdf:
948 name: stirling-pdf
949 driver: bridge
950 driver_opts:
951 com.docker.network.bridge.name: stirling-pdf
952 thelounge:
953 name: thelounge
954 driver: bridge
955 driver_opts:
956 com.docker.network.bridge.name: thelounge
957 trivia:
958 name: trivia
959 driver: bridge
960 driver_opts:
961 com.docker.network.bridge.name: trivia
962 internal: true
963 vaultwarden:
964 name: vaultwarden
965 driver: bridge
966 driver_opts:
967 com.docker.network.bridge.name: vaultwarden
968 vaultwarden-db:
969 name: vaultwarden-db
970 driver: bridge
971 driver_opts:
972 com.docker.network.bridge.name: vaultwarden-db
973 internal: true
974 wastebin:
975 name: wastebin
976 driver: bridge
977 driver_opts:
978 com.docker.network.bridge.name: wastebin
979 internal: true