Compare commits

...

97 Commits

Author SHA1 Message Date
forust fd5ea6cadd upd: kener v4.0.23
Deploy to Server / deploy (push) Failing after 0s
2026-04-10 02:29:02 +02:00
forust aa3598ee3d Merge pull request 'upd: updated kener to v4' (#14) from upd/kener-v4 into main
Deploy to Server / deploy (push) Failing after 1s
Reviewed-on: #14
2026-02-27 13:06:36 +01:00
forust c0205fa49b upd: updated kener to v4 2026-02-27 13:05:56 +01:00
forust a22707770f feat: update certificates logic:
Deploy to Server / deploy (push) Failing after 1s
- Switched xdfnx's homepage certificates to traefik-managed mode
- Renamed local fallback certs
2026-02-25 21:12:44 +01:00
forust 646f988a86 chore: Pinned stable kener version
Deploy to Server / deploy (push) Failing after 5s
2026-02-24 12:09:06 +01:00
forust 414d17d839 deleted test gitea workflow
Deploy to Server / deploy (push) Successful in 1s
2026-02-22 03:53:51 +01:00
forust 812e4eb87a ci: add copy of the gitea workflow for the github
Deploy to Server / deploy (push) Successful in 1s
2026-02-22 03:37:45 +01:00
forust 70b3b203fb fix: update email address
Deploy to Server / deploy (push) Has been cancelled
2026-02-22 00:28:19 +01:00
forust d857f3838d revert testing message
Deploy to Server / deploy (push) Successful in 1s
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
2026-02-20 20:41:01 +01:00
forust f8620349a9 fix deploy pipeline to prevent logspam
Deploy to Server / deploy (push) Successful in 16s
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
2026-02-20 20:38:39 +01:00
forust b1acff5c85 ci: add run-name to deployment workflow
Deploy to Server / deploy (push) Successful in 19s
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
2026-02-20 20:25:17 +01:00
forust beb6dca6a2 CI test change 2026-02-20 20:23:11 +01:00
forust aed6ff31f7 fix: add .runner (act runner settings) to .gitignore 2026-02-20 20:22:10 +01:00
forust 73684af21b ci: deploy workflow with local act_runner
Deploy to Server / deploy (push) Successful in 1s
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
2026-02-20 20:09:26 +01:00
forust cd5c90adcc Merge branch 'main' into ci/gitea-actions 2026-02-20 20:08:48 +01:00
forust 578a1ca544 revert: naio needs insecure transport
2593b54402 chore: add backquotes for downtify traefik labels removed insecureTransport
2026-02-20 01:25:23 +01:00
forust 12ed20a306 fix: support for external dynamic traefik configs 2026-02-20 01:16:54 +01:00
forust 400e7b6595 Starting learning cicd from scratch
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 8s
Demo gitea action
2026-02-19 00:36:02 +01:00
forust f58e96a25d add support for external dynamic traefik configs 2026-02-07 01:56:10 +01:00
forust a3e5f5a78b fix: let traefik handle https to portainer 2026-02-05 02:42:39 +01:00
forust 1a09a62a4c chore: deploy and lint workflows 2026-02-05 02:17:47 +01:00
forust 2593b54402 chore: add backquotes for downtify traefik labels
removed insecureTransport
2026-02-05 01:33:04 +01:00
forust 42826a037c refactor: store n8n data in named volumes 2026-02-04 22:26:43 +01:00
forust d7a68237e5 chore: remove redundant or unnececary traefik labels
- traefik.docker.network= (defined by traefik cli)
- traefik.http.routers.<routername>.middlewares=security-headers@file" (applied globally by traefik cli)
2026-02-04 22:26:27 +01:00
forust bbb8bdf0a7 feat: traefik routers for netronome, exported netronome env to .env 2026-02-04 11:00:44 +01:00
forust f4d3bd9c6d feat: basic netronome service 2026-02-03 14:25:53 +01:00
forust 6b919df7c6 chore: restart downtify service unless stopped 2026-02-01 17:24:50 +01:00
forust 3ee0b96ed5 feat: add acl policies to the headscale 2026-02-01 01:14:58 +01:00
forust d25d213d9b feat: add headscale admin web admin (along with headplane accessible via port) 2026-01-31 13:38:33 +01:00
forust a3b7c4c889 commit team avatars (and love) 2026-01-29 21:19:34 +01:00
forust 3dbb50c924 json logging 2026-01-29 17:30:09 +01:00
forust 54da82432b fix: remove headplane healtcheck (always false negative) 2026-01-29 17:22:47 +01:00
forust e5eb234c41 chore: gitignore for adguard 2026-01-29 17:22:40 +01:00
forust 721348e67f refactor: switch adguard data to named volume 2026-01-29 17:22:33 +01:00
forust d58ae2a5e7 chore: new statuspage for errorpages 2026-01-25 23:41:42 +01:00
forust aa516c3445 Merge pull request 'fix: errorpage styles fixes #11' (#12) from fix/errorpage-styles into main
Reviewed-on: #12
2026-01-25 23:39:10 +01:00
forust b5cc7d9a0d fix: errorpage styles fixes #11 2026-01-25 23:38:34 +01:00
forust 5dfa9c879b chore: comment-out errorpage ports 2026-01-24 01:24:18 +01:00
forust 3c5702a0fb Merge pull request 'feat/kener' (#9) from feat/kener into main
Reviewed-on: #9
2026-01-24 01:19:52 +01:00
forust dd0ca27f4f fix: add TZ env for checkmk 2026-01-24 01:16:24 +01:00
forust 7f7d0de090 fix: comment out kener ports 2026-01-24 00:58:09 +01:00
forust bee3f16cb2 fix: set restart policy of kener to Unelss stopped 2026-01-24 00:55:43 +01:00
forust 303d23968d fix: set restart policy of errorpages to Unelss stopped 2026-01-24 00:53:41 +01:00
forust b7ecf88052 fix: correct metube local router rule and fix TLS configuration comments 2026-01-23 17:54:00 +01:00
forust 5068591b8b fix: xdfnx's certificate 2026-01-23 17:48:00 +01:00
forust 8e57f21e44 feat: add traefik routers for kener (status subdomain) 2026-01-23 11:26:55 +01:00
forust 073d9ff569 feat: add initial kener instance (uptime monitoring) 2026-01-23 11:12:36 +01:00
forust c6d00e525b fix: comment-out CF Origin CA. (google trust service) 2026-01-22 00:51:15 +01:00
forust 539994a145 Merge branch 'feat/error-page' 2026-01-21 20:14:26 +01:00
forust 95f0afbbee feat: add traefik integration for error-handler
TODO: fix css
2026-01-21 20:14:02 +01:00
forust 9f86bd1142 feat: add errorpage-handler service (403,404, 500, 502-504) 2026-01-21 17:39:14 +01:00
forust af9668e8e6 md: archive overlayfs incident 2026-01-21 17:33:18 +01:00
forust 53b71a33ad fix: correct adguard traefik devrule 2026-01-21 11:41:37 +01:00
forust bf72007b14 feat: headplane (ui for headscale) 2026-01-21 10:09:31 +01:00
forust 0bad0a817e fix: bad request
Revert "chore: compose cleanup:"

This reverts nextcloud service's changes from commit 45ce789f58.
2026-01-20 15:11:06 +01:00
forust 525fed3b92 Merge pull request 'chore/compose-cleanup' (#8) from chore/compose-cleanup into main
Reviewed-on: #8
2026-01-20 00:17:42 +01:00
forust fe5e5c5e35 fix: aborted connection to portainer 2026-01-20 00:08:18 +01:00
forust 72aa022048 fix: "http plaintext sent to https" 2026-01-20 00:05:10 +01:00
forust f18d4d9be4 chore: use volumes to store data of
- portainer
- headscale
- termix
- uptime-kuma
- dockmon
2026-01-19 23:58:26 +01:00
forust 45ce789f58 chore: compose cleanup:
- Remove TZ envs
- +- unified compose structure
- Minify where possible
- Remove <service>.internal routers
- Remove service specifications where possible
Affected services:
- adguardhome
- authentik
- cfddns
- checkmk
- dockmon
- downtify
- gitea
- glance
- headscale
- homepages
- metube
- nextcloud
- penpot
- portainer
- termix
- traefik
- uptime-kuma

TODO: Move data from directory to volumes
2026-01-19 23:33:50 +01:00
forust d7c05fd058 fix: use webinar links to detect duplicates, supress logspam from telegram bot 2026-01-19 17:01:23 +01:00
forust c13056fba1 Merge pull request 'feat/checkmk' (#7) from feat/checkmk into main
Reviewed-on: #7
2026-01-19 15:59:21 +01:00
forust 5fe8af82d5 Revert "fix: clean up traefik configuration and add redirect middleware"
This reverts commit dc7fe64fbc.
2026-01-19 11:18:36 +01:00
forust 6d97246997 fix: change checkmk container restart policy to 'unless-stopped' 2026-01-19 00:08:16 +01:00
forust 6970279311 fix: remove checkmk port for docker cmk agent, using external agent 2026-01-18 23:09:41 +01:00
forust 02f4e0ab42 chore: switch to named volumes for site storing 2026-01-18 22:39:48 +01:00
forust 4a25622552 Merge branch 'main' of ssh://gitssh.forust.xyz:2221/forust/homelab into feat/checkmk 2026-01-18 19:28:12 +01:00
forust 0138fbd276 fix: update middleware for dev router in glance compose file 2026-01-18 19:24:41 +01:00
forust 94b5f39207 make glance dashboard public again 2026-01-18 00:19:39 +01:00
forust 403e88d548 fix: update NEXTCLOUD_DATADIR path to match new hardware 2026-01-17 17:44:11 +01:00
forust d03a4844fb chore: remove unused tailscale setup 2026-01-17 15:38:27 +01:00
forust 48ff08529e Merge pull request 'feat: add group support for webinar notifier' (#5) from feat/edu-group into main
Reviewed-on: #5
2026-01-14 16:46:37 +01:00
forust 81592b6142 feat: add group support for webinar notifier 2026-01-13 00:24:04 +01:00
forust 9480576966 fix: update bots' code to match .env keys 2026-01-12 15:33:42 +01:00
forust 9b43a9bef4 feat: add traefik configuration for external fileservers 2026-01-09 14:48:42 +01:00
forust 2b03335af5 chore: shorten aio subdomain 2026-01-06 19:04:23 +01:00
forust ea738ec14c Merge pull request 'add pgp pubkey to landing page, minor info changes' (#4) from chore/pgp into main
Reviewed-on: #4
2026-01-03 02:26:04 +01:00
forust e4e9d96a0b add pgp pubkey to landing page, minor info changes 2026-01-03 02:25:12 +01:00
forust 89576032b9 fix: update DNS settings for headscale 2026-01-03 01:39:05 +01:00
forust a9edfc0a25 fix: traefik middlewares for local and dev envs 2026-01-02 13:06:49 +01:00
forust acb8009307 Merge pull request 'feat: headscale implementation' (#3) from feat/tailscale into main
Reviewed-on: #3
2026-01-02 03:06:50 +01:00
forust 21f4e46028 chore: container name change for headscale web panel 2026-01-02 03:06:24 +01:00
forust 0234524635 feat: headscale implementation
- Headscale server
- Using goodieshq/headscale-admin as ui
- UI protected by security-chain
- Metrics available at /metrics
2026-01-02 01:29:51 +01:00
forust 26f5fb2fb9 Revert "feat: add gitea-basic-runner"
This reverts commit f0682319a7.
2026-01-02 00:38:04 +01:00
forust 122e6f6986 Revert "chore: gitea runners gitignored"
This reverts commit e0f7ab6561.
2026-01-02 00:37:47 +01:00
forust ce0bc4613a fix: headscale command 2026-01-01 03:19:45 +01:00
forust 8f4f460ff3 feat: add headscale server and UI configuration with Traefik routing 2026-01-01 02:39:28 +01:00
forust c048efd569 Merge branch 'main' of ssh://gitssh.forust.xyz:2221/forust/homelab into feat/tailscale 2026-01-01 01:45:57 +01:00
forust 1f1e13ff39 WIP: fix: update checkmk configuration for correct routing 2026-01-01 00:25:17 +01:00
forust c80a6c5351 Merge branch 'main' of ssh://gitssh.forust.xyz:2221/forust/homelab into feat/checkmk 2025-12-31 21:19:42 +01:00
forust db0f0f7bb6 feat: tailscale server 2025-12-14 02:43:00 +01:00
forust 5e4c60bb30 chore (security): port hardening, commented out non-critical ports 2025-12-14 02:42:46 +01:00
forust bfb21adff7 using local directory for storing chkmk data 2025-12-07 22:06:30 +01:00
forust 1c75382a6e fix: replace container_name to avoid misunderstandings 2025-12-07 04:14:56 +01:00
forust 9c5e037567 refactor: switch to official checkmk dockercompose 2025-12-07 04:10:32 +01:00
forust 9f784d2c31 chore: gitignore checkmk's files 2025-12-07 02:59:18 +01:00
forust a07e27bff6 feat: checkmk service 2025-12-07 02:44:12 +01:00
55 changed files with 2334 additions and 378 deletions
+39
View File
@@ -0,0 +1,39 @@
name: Deploy to Server
run-name: Deploying to ${{ runner.os}} server on ${{ gitea.ref }}
on:
push:
branches:
- main
- ci/gitea-actions
jobs:
deploy:
runs-on: prod
steps:
- name: Fetch and Diff Analysis
id: diff
run: |
cd ${{ secrets.PROD_DIR }}
git fetch origin main
CHANGES=$(git diff --name-only HEAD origin/main | cut -d/ -f1 | sort -u | tr '\n' ' ')
echo "dirs=$CHANGES" >> $GITHUB_OUTPUT
echo "Changed dirs: $CHANGES"
- name: Sync Server Files
run: |
cd ${{ secrets.PROD_DIR }}
git reset --hard origin/main
echo "Server files synced with origin/main"
- name: Deploy Services
run: |
cd ${{ secrets.PROD_DIR }}
for dir in ${{ steps.diff.outputs.dirs }}; do
if [ -d "$dir" ] && ([ -f "$dir/compose.yaml" ] || [ -f "$dir/docker-compose.yaml" ]); then
echo ">>> Deploying $dir"
cd "$dir"
DOCKER_BUILDKIT=1 BUILDKIT_PROGRESS=plain docker compose up -d --build --no-color
cd ..
else
echo ">>> Skipping $dir: no compose file found"
fi
done
+41
View File
@@ -0,0 +1,41 @@
## BINARY MODE, USE WITH THE GITHUB RUNNER BINARY INSTALLED ON THE SERVER
name: Deploy to Server
run-name: Deploying onto server on ${{ github.ref }}
on:
push:
branches:
- main
- ci/actions
jobs:
deploy:
runs-on: [prod, self-hosted]
steps:
- name: Fetch and Diff Analysis
id: diff
run: |
cd ${{ secrets.PROD_DIR }}
git fetch origin main
CHANGES=$(git diff --name-only HEAD origin/main | cut -d/ -f1 | sort -u | tr '\n' ' ')
echo "dirs=$CHANGES" >> $GITHUB_OUTPUT
echo "Changed dirs: $CHANGES"
- name: Sync Server Files
run: |
cd ${{ secrets.PROD_DIR }}
git reset --hard origin/main
echo "Server files synced with origin/main"
- name: Deploy Services
run: |
cd ${{ secrets.PROD_DIR }}
for dir in ${{ steps.diff.outputs.dirs }}; do
if [ -d "$dir" ] && ([ -f "$dir/compose.yaml" ] || [ -f "$dir/docker-compose.yaml" ]); then
echo ">>> Deploying $dir"
cd "$dir"
DOCKER_BUILDKIT=1 BUILDKIT_PROGRESS=plain docker compose up -d --build --no-color
cd ..
else
echo ">>> Skipping $dir: no compose file found"
fi
done
+20 -10
View File
@@ -2,20 +2,15 @@
sync.ffs_lock
.sync.ffs_db
# Environment
.env
.env.anna
.env.forust
.env.*
!.env.*example
# Copyparty
*.hist/
# Volumes and data directories
gitea/gitea-db/
gitea/gitea-data/*
gitea/*runner/*
n8n/n8n-data/*
n8n/n8n-node-data/*
adguardhome/data/*
adguardhome/conf/*
dockmon/data/*
portainer/portainer_data/*
metube/MeTube_downloads
@@ -24,6 +19,8 @@ termix/termix-data/*
cfddns/config.json
checkmk/checkmk/*
downtify/Downtify_downloads
headscale/config/*
headscale/data/*
# Steaming services files
streaming/jellyfin/*
@@ -35,11 +32,15 @@ streaming/qbittorrent/*
streaming/prowlarr/*
# Homepage
homepages/forust_files/assets/images/team/*
homepages/forust_files/.well-known/*
# Traefik files
traefik/letsencrypt/acme.json
traefik/dynamic/fileservers.yml
traefik/dynamic/*.local.y*ml.*
traefik/dynamic/*.external.y*ml
traefik/logs/*
# SSL Certificates
@@ -79,6 +80,8 @@ replacements.txt
# Git
.gitattributes
# Gitea/github Runners
.runner
# Misc
.DS_Store
@@ -87,3 +90,10 @@ replacements.txt
# Temp files
edu_master/temp/
temp/*
# Environment
.env
.env.anna
.env.forust
.env.*
!*example
+11 -19
View File
@@ -9,48 +9,40 @@ services:
- "853:853/tcp" # DNS over TLS
# - "67:67/udp" # DHCP
# - "68:68/tcp" # DHCP
- "3000:3000/tcp"
# - "3000:3000/tcp"
volumes:
- ./data/work:/opt/adguardhome/work
- ./data/conf:/opt/adguardhome/conf
- data:/opt/adguardhome/work
- ./conf:/opt/adguardhome/conf
- ./certs:/certs:ro
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.services.adguard.loadbalancer.server.port=3000"
# Prod Router
- "traefik.http.routers.adguard.rule=Host(`dns.forust.xyz`) || Host(`adguard.forust.xyz`)"
- "traefik.http.routers.adguard.entrypoints=websecure"
- "traefik.http.routers.adguard.middlewares=security-headers@file"
- "traefik.http.routers.adguard.service=adguard"
- "traefik.http.routers.adguard.tls=true"
# Local Router
- "traefik.http.routers.adguard-local.rule=Host(`dns.workstation.internal`) || Host(`adguard.internal`) || Host(`adguard.workstation.internal`)"
- "traefik.http.routers.adguard-local.rule=Host(`adguard.workstation.internal`) || Host(`dns.workstation.internal`)"
- "traefik.http.routers.adguard-local.entrypoints=websecure"
- "traefik.http.routers.adguard-local.middlewares=security-headers@file"
- "traefik.http.routers.adguard-local.service=adguard"
- "traefik.http.routers.adguard-local.tls=true"
# Dev Router
- "traefik.http.routers.adguard-dev.rule=Host(`dns.gigaforust.internal`) || Host(`adguard.gigaforust.internal`)"
- "traefik.http.routers.adguard-dev.rule=Host(`adguard.gigaforust.internal`) || Host(`dns.gigaforust.internal`)"
- "traefik.http.routers.adguard-dev.entrypoints=websecure"
- "traefik.http.routers.adguard-dev.middlewares=security-headers@file"
- "traefik.http.routers.adguard-dev.service=adguard"
- "traefik.http.routers.adguard-dev.tls=true"
# DoH
# DoH Router
- "traefik.http.routers.dns.rule=(Host(`dns.forust.xyz`) && PathPrefix(`/dns-query`))"
- "traefik.http.routers.dns.entrypoints=websecure"
- "traefik.http.routers.dns.service=adguard"
- "traefik.http.routers.dns.tls.certresolver=letsencrypt"
# Glance Metadata
- glance.name=adguard
- glance.url=https://adguard.forust.xyz/
- glance.description=AdGuard Home is a network-wide software for blocking ads.
networks:
- proxy
volumes:
data:
networks:
proxy:
external: true
+7 -18
View File
@@ -26,9 +26,9 @@ services:
command: server
container_name: authentik-server
restart: unless-stopped
ports:
- ${PORT_HTTP:-9000}:9000
- ${PORT_HTTPS:-9443}:9443
# ports:
# - ${PORT_HTTP:-9000}:9000
# - ${PORT_HTTPS:-9443}:9443
env_file:
- .env
environment:
@@ -37,37 +37,26 @@ services:
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
volumes:
- ./media:/media
- ./custom-templates:/templates
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# Services
# - "traefik.http.services.authentik-server.loadbalancer.server.port=9443"
- "traefik.http.services.authentik-server.loadbalancer.server.port=9000"
# Prod Router
- "traefik.http.routers.authentik-server.rule=Host(`auth.forust.xyz`)"
- "traefik.http.routers.authentik-server.entrypoints=websecure"
- "traefik.http.routers.authentik-server.middlewares=security-headers@file"
- "traefik.http.routers.authentik-server.service=authentik-server"
- "traefik.http.routers.authentik-server.tls=true"
# Local Router
- "traefik.http.routers.authentik-server-local.rule=Host(`auth.workstation.internal`) || Host(`auth-dashboard.internal`)"
- "traefik.http.routers.authentik-server-local.rule=Host(`auth.workstation.internal`)"
- "traefik.http.routers.authentik-server-local.entrypoints=websecure"
- "traefik.http.routers.authentik-server-local.middlewares=security-headers@file"
- "traefik.http.routers.authentik-server-local.service=authentik-server"
- "traefik.http.routers.authentik-server-local.tls=true"
# Dev Router
- "traefik.http.routers.authentik-server-dev.rule=Host(`auth.gigaforust.internal`)"
- "traefik.http.routers.authentik-server-dev.entrypoints=websecure"
- "traefik.http.routers.authentik-server-dev.middlewares=security-headers@file"
- "traefik.http.routers.authentik-server-dev.service=authentik-server"
- "traefik.http.routers.authentik-server-dev.tls=true"
volumes:
- ./media:/media
- ./custom-templates:/templates
networks:
- proxy
- authentik
+1 -1
View File
@@ -2,6 +2,7 @@ services:
cloudflare-ddns:
image: timothyjmiller/cloudflare-ddns:latest
container_name: cloudflare-ddns
restart: unless-stopped
security_opt:
- no-new-privileges:true
network_mode: 'host'
@@ -10,4 +11,3 @@ services:
- PGID=1000
volumes:
- ./config.json:/config.json
restart: unless-stopped
+2
View File
@@ -0,0 +1,2 @@
CMK_PASSWORD=password
TZ=Europe/Berlin
+39
View File
@@ -0,0 +1,39 @@
services:
checkmk:
image: "checkmk/check-mk-raw:2.4.0-latest"
container_name: "checkmk"
restart: unless-stopped
# ports:
# - 5000:5000
# - 6776:8000
volumes:
- sites:/omd/sites
tmpfs:
- /opt/omd/sites/cmk/tmp:uid=1000,gid=1000
environment:
- CMK_PASSWORD=${CMK_PASSWORD:-password}
- CMK_SITE_ID=cmk
- TZ=${TZ:-Etc/UTC}
labels:
- "traefik.enable=true"
- "traefik.http.services.checkmk.loadbalancer.server.port=5000"
# Prod Router
- "traefik.http.routers.checkmk.rule=Host(`cmk.forust.xyz`)"
- "traefik.http.routers.checkmk.entrypoints=websecure"
- "traefik.http.routers.checkmk.tls=true"
# Local Router
- "traefik.http.routers.checkmk-local.rule=Host(`cmk.workstation.internal`)"
- "traefik.http.routers.checkmk-local.entrypoints=websecure"
- "traefik.http.routers.checkmk-local.tls=true"
# Dev Router
- "traefik.http.routers.checkmk-dev.rule=Host(`cmk.gigaforust.internal`)"
- "traefik.http.routers.checkmk-dev.entrypoints=websecure"
- "traefik.http.routers.checkmk-dev.tls=true"
networks:
- proxy
networks:
proxy:
external: true
volumes:
sites:
+12 -20
View File
@@ -3,52 +3,44 @@ services:
image: darthnorse/dockmon:latest
container_name: dockmon
restart: unless-stopped
ports:
- 8000:443
environment:
- TZ=Europe/Bratislava
# ports:
# - 8000:443
volumes:
- ./data:/app/data
- data:/app/data
- /var/run/docker.sock:/var/run/docker.sock
healthcheck:
test: [ "CMD", "curl", "-k", "-f", "https://localhost:443/health" ]
interval: 30s
timeout: 10s
retries: 3
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.services.dockmon.loadbalancer.server.port=443"
- "traefik.http.services.dockmon.loadbalancer.server.scheme=https"
- "traefik.http.services.dockmon.loadbalancer.serverstransport=insecureTransport@file"
# Prod Router
- "traefik.http.routers.dockmon.rule=Host(`dockmon.forust.xyz`)"
- "traefik.http.routers.dockmon.entrypoints=websecure"
- "traefik.http.routers.dockmon.middlewares=security-chain@file"
- "traefik.http.routers.dockmon.service=dockmon"
- "traefik.http.routers.dockmon.tls=true"
- "traefik.http.services.dockmon.loadbalancer.server.port=443"
- "traefik.http.services.dockmon.loadbalancer.server.scheme=https"
- "traefik.http.services.dockmon.loadbalancer.serverstransport=insecureTransport@file"
# Local Router
- "traefik.http.routers.dockmon-local.rule=Host(`dockmon.workstation.internal`) || Host(`dockmon.internal`)"
- "traefik.http.routers.dockmon-local.rule=Host(`dockmon.workstation.internal`)"
- "traefik.http.routers.dockmon-local.entrypoints=websecure"
- "traefik.http.routers.dockmon-local.middlewares=security-headers@file"
- "traefik.http.routers.dockmon-local.service=dockmon"
- "traefik.http.routers.dockmon-local.tls=true"
# Dev Router
- "traefik.http.routers.dockmon-dev.rule=Host(`dockmon.gigaforust.internal`)"
- "traefik.http.routers.dockmon-dev.entrypoints=websecure"
- "traefik.http.routers.dockmon-dev.middlewares=security-chain@file"
- "traefik.http.routers.dockmon-dev.service=dockmon"
- "traefik.http.routers.dockmon-dev.tls=true"
# Glance Metadata
- glance.name=dockmon
- glance.url=https://dockmon.forust.xyz/
- glance.description=Dockmon is a lightweight Docker container monitoring and management tool with a user-friendly web interface.
networks:
- proxy
volumes:
data:
networks:
proxy:
external: true
+19 -27
View File
@@ -2,38 +2,30 @@ services:
downtify:
container_name: downtify
image: ghcr.io/henriquesebastiao/downtify:latest
restart: unless-stopped
# ports:
# - '7077:8000'
labels:
- traefik.enable=true
- traefik.http.services.downtify.loadbalancer.server.port=8000
# Prod Router
- traefik.http.routers.downtify.rule=Host(`downtify.forust.xyz`)
- traefik.http.routers.downtify.entrypoints=websecure
- traefik.http.routers.downtify.middlewares=security-chain@file
- traefik.http.routers.downtify.service=downtify
- traefik.http.routers.downtify.tls=true
# Local Router
- traefik.http.routers.downtify-local.rule=Host(`downtify.workstation.internal`) || Host(`downtify.internal`)
- traefik.http.routers.downtify-local.entrypoints=websecure
- traefik.http.routers.downtify-local.middlewares=security-headers@file
- traefik.http.routers.downtify-local.service=downtify
- traefik.http.routers.downtify-local.tls=true
# Dev Router
- traefik.http.routers.downtify-dev.rule=Host(`downtify.gigaforust.internal`)
- traefik.http.routers.downtify-dev.entrypoints=websecure
- traefik.http.routers.downtify-dev.middlewares=security-chain@file
- traefik.http.routers.downtify-dev.service=downtify
- traefik.http.routers.downtify-dev.tls=true
networks:
- proxy
volumes:
- ./Downtify_downloads:/downloads
labels:
- "traefik.enable=true"
- "traefik.http.services.downtify.loadbalancer.server.port=8000"
# Prod Router
- "traefik.http.routers.downtify.rule=Host(`downtify.forust.xyz`)"
- "traefik.http.routers.downtify.entrypoints=websecure"
- "traefik.http.routers.downtify.middlewares=security-chain@file"
- "traefik.http.routers.downtify.tls=true"
# Local Router
- "traefik.http.routers.downtify-local.rule=Host(`downtify.workstation.internal`)"
- "traefik.http.routers.downtify-local.entrypoints=websecure"
- "traefik.http.routers.downtify-local.tls=true"
# Dev Router
- "traefik.http.routers.downtify-dev.rule=Host(`downtify.gigaforust.internal`)"
- "traefik.http.routers.downtify-dev.entrypoints=websecure"
- "traefik.http.routers.downtify-dev.tls=true"
networks:
- proxy
networks:
proxy:
external: true
+20 -9
View File
@@ -12,15 +12,26 @@ logging.basicConfig(
)
logger = logging.getLogger(__name__)
# Load configuration
LOGIN = os.getenv('EDU_LOGIN')
PASSWORD = os.getenv('EDU_PASSWORD')
URL_LOGIN = os.getenv('EDU_URL_LOGIN', 'https://edu.edu.vn.ua/user/login')
URL_VERIFY = os.getenv('EDU_URL_VERIFY', 'https://edu.edu.vn.ua/course/userlist')
INTERVAL = int(os.getenv('PHPSESSID_INTERVAL', 10))
USER_AGENT = os.getenv('USER_AGENT', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36')
REDIS_HOST = os.getenv('REDIS_HOST', 'redis')
REDIS_PORT = int(os.getenv('REDIS_PORT', 6379))
# Load configuration (adapted to .env keys)
def _env(key, default=None):
v = os.getenv(key, default)
if isinstance(v, str) and len(v) >= 2 and ((v[0] == '"' and v[-1] == '"') or (v[0] == "'" and v[-1] == "'")):
return v[1:-1]
return v
LOGIN = _env('KEEPER_LOGIN')
PASSWORD = _env('KEEPER_PASSWORD')
EDU_BASE = _env('EDU_URL_BASE', 'https://edu.edu.vn.ua')
EDU_LOGIN_PATH = _env('EDU_URL_LOGIN', '/user/login')
EDU_COURSES_PATH = _env('EDU_URL_COURSES', '/course/userlist')
URL_LOGIN = f"{EDU_BASE.rstrip('/')}/{EDU_LOGIN_PATH.lstrip('/')}"
URL_VERIFY = f"{EDU_BASE.rstrip('/')}/{EDU_COURSES_PATH.lstrip('/')}"
INTERVAL = int(_env('KEEPER_INTERVAL', 10))
USER_AGENT = _env('USER_AGENT', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36')
REDIS_HOST = _env('REDIS_HOST', 'redis')
REDIS_PORT = int(_env('REDIS_PORT', 6379))
SUCCESS_FILE = '/tmp/last_success'
+107 -31
View File
@@ -3,7 +3,8 @@ import logging
import redis
import json
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ChatMember
from telegram.constants import ChatType
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes
from playwright.async_api import async_playwright
@@ -14,22 +15,36 @@ logging.basicConfig(
)
logger = logging.getLogger(__name__)
# Suppress HTTP request logs
logging.getLogger('urllib3').setLevel(logging.WARNING)
logging.getLogger('httpx').setLevel(logging.WARNING)
logging.getLogger('telegram.ext._application').setLevel(logging.WARNING)
# Load environment variables
WEBINAR_URL = os.getenv('WEBINAR_URL', 'https://edu.edu.vn.ua/webinar/useractive')
WEBINAR_CHECK_INTERVAL = int(os.getenv('WEBINAR_CHECK_INTERVAL', 60))
REDIS_HOST = os.getenv('REDIS_HOST', 'redis')
REDIS_PORT = int(os.getenv('REDIS_PORT', 6379))
PLAYWRIGHT_WS = os.getenv('PLAYWRIGHT_WS', 'ws://playwright-service:3000/ws')
USER_AGENT = os.getenv('USER_AGENT', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36')
WEBINAR_TELEGRAM_TOKEN = os.getenv('WEBINAR_TELEGRAM_TOKEN')
ADMIN_ID = int(os.getenv('WEBINAR_ADMIN_ID', '0'))
def _env(key, default=None):
v = os.getenv(key, default)
if isinstance(v, str) and len(v) >= 2 and ((v[0] == '"' and v[-1] == '"') or (v[0] == "'" and v[-1] == "'")):
return v[1:-1]
return v
EDU_BASE = _env('EDU_URL_BASE', 'https://edu.edu.vn.ua')
EDU_WEBINAR_PATH = _env('EDU_URL_WEBINAR', '/webinar/useractive')
WEBINAR_URL = f"{EDU_BASE.rstrip('/')}/{EDU_WEBINAR_PATH.lstrip('/')}"
WEBINAR_CHECK_INTERVAL = int(_env('WEBINAR_CHECK_INTERVAL', 60))
REDIS_HOST = _env('REDIS_HOST', 'redis')
REDIS_PORT = int(_env('REDIS_PORT', 6379))
PLAYWRIGHT_WS = _env('PLAYWRIGHT_WS', 'ws://playwright-service:3000/ws')
USER_AGENT = _env('USER_AGENT', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36')
WEBINAR_TELEGRAM_TOKEN = _env('WEBINAR_TELEGRAM_TOKEN')
ADMIN_ID = int(_env('WEBINAR_ADMIN_ID', '0'))
# Redis Keys
KEY_WHITELIST = "bot:whitelist"
KEY_WHITELIST_ENABLED = "bot:whitelist_enabled"
KEY_SUBSCRIBERS = "bot:subscribers"
KEY_PHPSESSID = "EDU_PHPSESSID"
KEY_WEBINAR_HISTORY = "bot:webinar_history" # Stores last 5 webinars
KEY_WEBINAR_HISTORY = "bot:webinar_history" # Stores last 3 webinars
# Initialize Redis
try:
@@ -48,7 +63,7 @@ TRANSLATIONS = {
'welcome_admin': "\n\n👑 <b>Режим администратора активен</b>",
'access_denied': "⛔ Доступ запрещен. Вас нет в белом списке.",
'help_title': "🤖 <b>Помощь по боту</b>\n\n",
'help_commands': "/start - Подписаться на уведомления\n/help - Показать это сообщение\n/language - Сменить язык",
'help_commands': "/start - Подписаться на уведомления\n/stop - Отписаться от уведомлений\n/help - Показать это сообщение\n/language - Сменить язык",
'help_admin': "\n<b>Команды администратора:</b>\n/adduser [user_id] - Добавить пользователя в белый список\n/removeuser [user_id] - Удалить пользователя из белого списка\nИли используйте панель ниже для управления настройками.",
'admin_only': "⛔ Только для администратора!",
'user_added': "✅ Пользователь {user_id} добавлен в белый список",
@@ -79,13 +94,15 @@ TRANSLATIONS = {
'flag_ru': "🇷🇺 Русский",
'flag_uk': "🇺🇦 Українська",
'flag_en': "🇬🇧 English",
'history_cleared': "✅ История вебинаров очищена",
'history_clear_failed': "❌ Ошибка при очистке истории",
},
'uk': {
'welcome': "👋 Привіт, {name}!\n\nЯ бот-сповіщувач про вебінари. Я повідомлятиму вас, коли з'явиться новий вебінар.\nВи підписані на сповіщення.",
'welcome_admin': "\n\n👑 <b>Режим адміністратора активний</b>",
'access_denied': "⛔ Доступ заборонено. Вас немає в білому списку.",
'help_title': "🤖 <b>Довідка по боту</b>\n\n",
'help_commands': "/start - Підписатися на сповіщення\n/help - Показати це повідомлення\n/language - Змінити мову",
'help_commands': "/start - Підписатися на сповіщення\n/stop - Відписатися від сповіщень\n/help - Показати це повідомлення\n/language - Змінити мову",
'help_admin': "\n<b>Команди адміністратора:</b>\n/adduser [user_id] - Додати користувача до білого списку\n/removeuser [user_id] - Видалити користувача з білого списку\nАбо використовуйте панель нижче для керування налаштуваннями.",
'admin_only': "⛔ Тільки для адміністратора!",
'user_added': "✅ Користувач {user_id} доданий до білого списку",
@@ -116,13 +133,15 @@ TRANSLATIONS = {
'flag_ru': "🇷🇺 Русский",
'flag_uk': "🇺🇦 Українська",
'flag_en': "🇬🇧 English",
'history_cleared': "✅ Історія вебінарів очищена",
'history_clear_failed': "❌ Помилка при очищенні історії",
},
'en': {
'welcome': "👋 Hello, {name}!\n\nI am the Webinar Checker Bot. I will notify you when a new webinar appears.\nYou have been subscribed to notifications.",
'welcome_admin': "\n\n👑 <b>Admin Mode Active</b>",
'access_denied': "⛔ Access denied. You are not on the whitelist.",
'help_title': "🤖 <b>Bot Help</b>\n\n",
'help_commands': "/start - Subscribe to notifications\n/help - Show this message\n/language - Change language",
'help_commands': "/start - Subscribe to notifications\n/stop - Unsubscribe from notifications\n/help - Show this message\n/language - Change language",
'help_admin': "\n<b>Admin Commands:</b>\n/adduser [user_id] - Add user to whitelist\n/removeuser [user_id] - Remove user from whitelist\nOr use the panel below to manage settings.",
'admin_only': "⛔ Admin only!",
'user_added': "✅ User {user_id} added to whitelist",
@@ -153,6 +172,8 @@ TRANSLATIONS = {
'flag_ru': "🇷🇺 Русский",
'flag_uk': "🇺🇦 Українська",
'flag_en': "🇬🇧 English",
'history_cleared': "✅ Webinar history cleared",
'history_clear_failed': "❌ Error clearing history",
}
}
@@ -203,6 +224,21 @@ def is_whitelisted(user_id: int) -> bool:
return redis_client.sismember(KEY_WHITELIST, str(user_id))
async def is_group_admin(update: Update, context: ContextTypes.DEFAULT_TYPE) -> bool:
"""Check if the user is an administrator in the group."""
user = update.effective_user
chat = update.effective_chat
if chat.type in [ChatType.PRIVATE, "private"]:
return True
try:
member = await context.bot.get_chat_member(chat.id, user.id)
return member.status in [ChatMember.OWNER, ChatMember.ADMINISTRATOR]
except Exception as e:
logger.error(f"Failed to check admin status: {e}")
return False
def get_admin_keyboard(user_id: int):
"""Generate admin panel keyboard."""
whitelist_enabled = redis_client.get(KEY_WHITELIST_ENABLED) != "0"
@@ -221,19 +257,21 @@ def get_admin_keyboard(user_id: int):
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /start command."""
user = update.effective_user
logger.info(f"User {user.id} ({user.username}) started the bot.")
chat = update.effective_chat
logger.info(f"User {user.id} ({user.username}) started the bot in chat {chat.id} ({chat.type}).")
# Check whitelist - MUST be the user executing the command
if not is_whitelisted(user.id):
await update.message.reply_text(t(user.id, 'access_denied'))
return
# Add to subscribers
redis_client.sadd(KEY_SUBSCRIBERS, user.id)
# Add to subscribers (Chat ID!)
redis_client.sadd(KEY_SUBSCRIBERS, chat.id)
msg = t(user.id, 'welcome', name=user.first_name)
msg = t(chat.id, 'welcome', name=user.first_name)
if user.id == ADMIN_ID:
msg += t(user.id, 'welcome_admin')
if user.id == ADMIN_ID and chat.type == "private":
msg += t(chat.id, 'welcome_admin')
await update.message.reply_text(msg, parse_mode='HTML', reply_markup=get_admin_keyboard(user.id))
else:
await update.message.reply_text(msg, parse_mode='HTML')
@@ -241,19 +279,39 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /help command."""
user_id = update.effective_user.id
msg = t(user_id, 'help_title') + t(user_id, 'help_commands')
chat_id = update.effective_chat.id
msg = t(chat_id, 'help_title') + t(chat_id, 'help_commands')
if user_id == ADMIN_ID:
msg += t(user_id, 'help_admin')
if user_id == ADMIN_ID and update.effective_chat.type == "private":
msg += t(chat_id, 'help_admin')
await update.message.reply_text(msg, parse_mode='HTML', reply_markup=get_admin_keyboard(user_id))
else:
await update.message.reply_text(msg, parse_mode='HTML')
async def stop_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /stop command (unsubscribe)."""
user = update.effective_user
chat = update.effective_chat
# Permission check: Whitelisted user OR Group Admin
if not (is_whitelisted(user.id) or await is_group_admin(update, context)):
await update.message.reply_text(t(chat.id, 'access_denied')) # Or specific "admin only" message
return
redis_client.srem(KEY_SUBSCRIBERS, chat.id)
await update.message.reply_text(t(chat.id, 'whitelist_disabled').replace(" whitelist", " notifications").replace("Білий список", "Сповіщення").replace("Белый список", "Уведомления") if chat.id else "Unsubscribed")
async def language_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /language command."""
user_id = update.effective_user.id
user = update.effective_user
chat = update.effective_chat
# Permission check for groups
if not (is_whitelisted(user.id) or await is_group_admin(update, context)):
return
await update.message.reply_text(
t(user_id, 'select_language'),
t(chat.id, 'select_language'),
parse_mode='HTML',
reply_markup=get_language_keyboard()
)
@@ -303,6 +361,21 @@ async def remove_user(update: Update, context: ContextTypes.DEFAULT_TYPE):
except ValueError:
await update.message.reply_text(t(admin_id, 'invalid_user_id'))
async def clear_history(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Clear webinar history (admin only)."""
admin_id = update.effective_user.id
if admin_id != ADMIN_ID:
await update.message.reply_text(t(admin_id, 'admin_only'))
return
try:
redis_client.delete(KEY_WEBINAR_HISTORY)
await update.message.reply_text(t(admin_id, 'history_cleared'))
logger.info("Admin cleared webinar history")
except Exception as e:
logger.error(f"Failed to clear history: {e}")
await update.message.reply_text(t(admin_id, 'history_clear_failed'))
# --- Admin Callbacks ---
async def admin_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
@@ -363,9 +436,9 @@ async def language_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
# --- Webinar Checking Job ---
def get_webinar_key(name: str, url: str) -> str:
"""Generate unique key for a webinar based on name and URL."""
return f"{name}|{url}"
def get_webinar_key(url: str) -> str:
"""Generate unique key for a webinar based on URL."""
return url
def get_stored_webinars() -> list:
"""Get list of stored webinar keys from Redis."""
@@ -378,9 +451,9 @@ def get_stored_webinars() -> list:
return []
def store_webinars(webinar_keys: list):
"""Store up to 5 most recent webinar keys in Redis."""
# Keep only last 5
webinar_keys = webinar_keys[-5:]
"""Store up to 3 most recent webinar keys in Redis."""
# Keep only last 3
webinar_keys = webinar_keys[-3:]
try:
redis_client.set(KEY_WEBINAR_HISTORY, json.dumps(webinar_keys))
logger.info(f"Stored {len(webinar_keys)} webinar(s) in history")
@@ -515,7 +588,7 @@ async def check_webinars_job(context: ContextTypes.DEFAULT_TYPE):
current_keys = []
for webinar in current_webinars:
key = get_webinar_key(webinar['name'], webinar['url'])
key = get_webinar_key(webinar['url'])
current_keys.append(key)
if key not in stored_keys:
@@ -535,6 +608,7 @@ async def check_webinars_job(context: ContextTypes.DEFAULT_TYPE):
for sub_id in subscribers:
try:
# Build message in user's language
# sub_id comes from redis set as string, convert to int for translation lookup
webinar_items = "\n\n".join([
t(int(sub_id), 'webinar_item', name=w['name'], url=w['url'])
for w in new_webinars
@@ -569,10 +643,12 @@ def main():
# Handlers
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("stop", stop_command))
app.add_handler(CommandHandler("help", help_command))
app.add_handler(CommandHandler("language", language_command))
app.add_handler(CommandHandler("adduser", add_user))
app.add_handler(CommandHandler("removeuser", remove_user))
app.add_handler(CommandHandler("clearhistory", clear_history))
# Callback handlers - language selection first, then admin panel
app.add_handler(CallbackQueryHandler(language_callback, pattern="^lang_"))
+5
View File
@@ -0,0 +1,5 @@
FROM nginx:alpine
RUN rm -rf /usr/share/nginx/html/*
COPY html /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+19
View File
@@ -0,0 +1,19 @@
services:
errorpage:
build: .
container_name: error-pages
restart: unless-stopped
# ports:
# - 1234:80
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.services.error-pages.loadbalancer.server.port=80"
# Error handler middleware
- "traefik.http.middlewares.error-pages.errors.status=400-599"
- "traefik.http.middlewares.error-pages.errors.service=error-pages"
- "traefik.http.middlewares.error-pages.errors.query=/{status}.html"
networks:
proxy:
external: true
+208
View File
@@ -0,0 +1,208 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>403 // Forbidden</title>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* hidden in a plain sight? */
:root {
--bg-color: #050505;
--text-color: #e0e0e0;
--accent: #ffffff;
--dim: #666666;
--font-mono: 'Courier New', Courier, monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-mono);
line-height: 1.6;
font-size: 16px;
padding: 2rem;
}
a {
color: var(--text-color);
text-decoration: none;
border-bottom: 1px solid var(--dim);
transition: all 0.2s;
}
a:hover {
background-color: var(--text-color);
color: var(--bg-color);
border-color: var(--text-color);
}
.container {
max-width: 800px;
margin: 0 auto;
}
/* TEXT */
h1 {
font-size: 2.5rem;
text-transform: uppercase;
letter-spacing: -2px;
margin-bottom: 0.5rem;
}
h2 {
font-size: 1.2rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--dim);
display: inline-block;
padding-right: 20px;
}
.subtitle {
color: var(--dim);
margin-bottom: 2rem;
}
hr {
border: 0;
border-top: 1px dashed var(--dim);
margin: 2rem 0;
}
.comment {
color: var(--dim);
font-size: 0.9rem;
margin-left: 10px;
}
/* SECTIONS */
section {
margin-bottom: 3rem;
}
/* LISTS */
ul {
list-style: none;
}
.link-list li {
margin-bottom: 0.8rem;
display: flex;
align-items: center;
gap: 15px;
}
/* STACK GRID */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.skill-item {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.level {
font-weight: bold;
}
.special .level {
color: var(--text-color);
text-shadow: 1px 0 0 red, -1px 0 0 blue;
}
/* my dudes */
.team-grid {
display: flex;
gap: 2rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.member {
text-align: center;
width: 100px;
}
.avatar {
width: 80px;
height: 80px;
background-color: #222;
border: 2px solid var(--text-color);
margin: 0 auto 10px auto;
background-size: cover;
}
/* if no avatar added: */
.placeholder::before {
content: "?";
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 2rem;
color: var(--dim);
}
/* REPOS */
.repo-list li {
margin-bottom: 1rem;
}
/* FOOTER */
footer {
text-align: center;
color: var(--dim);
font-size: 0.8rem;
/* flag{why-are-you-here?} */
margin-top: 4rem;
}
/* SMTH RESPONSIVE */
@media (max-width: 600px) {
.grid-2 {
grid-template-columns: 1fr;
gap: 0;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1 class="glitch" data-text="403">403</h1>
<p class="subtitle">> Forbidden / Access Denied.</p>
</header>
<hr>
<section id="message">
<h2>./error_message</h2>
<p>You do not have permission to access this resource.</p>
<br>
<p>Check the <a href="https://status.forust.xyz">System Status</a> if you believe this is an
error.</p>
</section>
<footer>
<p>root@error:~$ sudo access_resource</p>
<p>User is not in the sudoers file. This incident will be reported.</p>
<p>&copy; XRock - Just Signal.</p>
</footer>
</div>
</body>
</html>
+207
View File
@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 // Not Found</title>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* hidden in a plain sight? */
:root {
--bg-color: #050505;
--text-color: #e0e0e0;
--accent: #ffffff;
--dim: #666666;
--font-mono: 'Courier New', Courier, monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-mono);
line-height: 1.6;
font-size: 16px;
padding: 2rem;
}
a {
color: var(--text-color);
text-decoration: none;
border-bottom: 1px solid var(--dim);
transition: all 0.2s;
}
a:hover {
background-color: var(--text-color);
color: var(--bg-color);
border-color: var(--text-color);
}
.container {
max-width: 800px;
margin: 0 auto;
}
/* TEXT */
h1 {
font-size: 2.5rem;
text-transform: uppercase;
letter-spacing: -2px;
margin-bottom: 0.5rem;
}
h2 {
font-size: 1.2rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--dim);
display: inline-block;
padding-right: 20px;
}
.subtitle {
color: var(--dim);
margin-bottom: 2rem;
}
hr {
border: 0;
border-top: 1px dashed var(--dim);
margin: 2rem 0;
}
.comment {
color: var(--dim);
font-size: 0.9rem;
margin-left: 10px;
}
/* SECTIONS */
section {
margin-bottom: 3rem;
}
/* LISTS */
ul {
list-style: none;
}
.link-list li {
margin-bottom: 0.8rem;
display: flex;
align-items: center;
gap: 15px;
}
/* STACK GRID */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.skill-item {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.level {
font-weight: bold;
}
.special .level {
color: var(--text-color);
text-shadow: 1px 0 0 red, -1px 0 0 blue;
}
/* my dudes */
.team-grid {
display: flex;
gap: 2rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.member {
text-align: center;
width: 100px;
}
.avatar {
width: 80px;
height: 80px;
background-color: #222;
border: 2px solid var(--text-color);
margin: 0 auto 10px auto;
background-size: cover;
}
/* if no avatar added: */
.placeholder::before {
content: "?";
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 2rem;
color: var(--dim);
}
/* REPOS */
.repo-list li {
margin-bottom: 1rem;
}
/* FOOTER */
footer {
text-align: center;
color: var(--dim);
font-size: 0.8rem;
/* flag{why-are-you-here?} */
margin-top: 4rem;
}
/* SMTH RESPONSIVE */
@media (max-width: 600px) {
.grid-2 {
grid-template-columns: 1fr;
gap: 0;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1 class="glitch" data-text="404">404</h1>
<p class="subtitle">> Page Not Found / Lost in the Void.</p>
</header>
<hr>
<section id="message">
<h2>./error_message</h2>
<p>The page you are looking for does not exist or has been moved.</p>
<br>
<p>Check the <a href="https://status.forust.xyz">System Status</a> if you believe this is an error.</p>
</section>
<footer>
<p>root@error:~$ ping target</p>
<p>Destination Host Unreachable</p>
<p>&copy; XRock - Just Signal.</p>
</footer>
</div>
</body>
</html>
+207
View File
@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>500 // Server Error</title>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* hidden in a plain sight? */
:root {
--bg-color: #050505;
--text-color: #e0e0e0;
--accent: #ffffff;
--dim: #666666;
--font-mono: 'Courier New', Courier, monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-mono);
line-height: 1.6;
font-size: 16px;
padding: 2rem;
}
a {
color: var(--text-color);
text-decoration: none;
border-bottom: 1px solid var(--dim);
transition: all 0.2s;
}
a:hover {
background-color: var(--text-color);
color: var(--bg-color);
border-color: var(--text-color);
}
.container {
max-width: 800px;
margin: 0 auto;
}
/* TEXT */
h1 {
font-size: 2.5rem;
text-transform: uppercase;
letter-spacing: -2px;
margin-bottom: 0.5rem;
}
h2 {
font-size: 1.2rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--dim);
display: inline-block;
padding-right: 20px;
}
.subtitle {
color: var(--dim);
margin-bottom: 2rem;
}
hr {
border: 0;
border-top: 1px dashed var(--dim);
margin: 2rem 0;
}
.comment {
color: var(--dim);
font-size: 0.9rem;
margin-left: 10px;
}
/* SECTIONS */
section {
margin-bottom: 3rem;
}
/* LISTS */
ul {
list-style: none;
}
.link-list li {
margin-bottom: 0.8rem;
display: flex;
align-items: center;
gap: 15px;
}
/* STACK GRID */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.skill-item {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.level {
font-weight: bold;
}
.special .level {
color: var(--text-color);
text-shadow: 1px 0 0 red, -1px 0 0 blue;
}
/* my dudes */
.team-grid {
display: flex;
gap: 2rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.member {
text-align: center;
width: 100px;
}
.avatar {
width: 80px;
height: 80px;
background-color: #222;
border: 2px solid var(--text-color);
margin: 0 auto 10px auto;
background-size: cover;
}
/* if no avatar added: */
.placeholder::before {
content: "?";
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 2rem;
color: var(--dim);
}
/* REPOS */
.repo-list li {
margin-bottom: 1rem;
}
/* FOOTER */
footer {
text-align: center;
color: var(--dim);
font-size: 0.8rem;
/* flag{why-are-you-here?} */
margin-top: 4rem;
}
/* SMTH RESPONSIVE */
@media (max-width: 600px) {
.grid-2 {
grid-template-columns: 1fr;
gap: 0;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1 class="glitch" data-text="500">500</h1>
<p class="subtitle">> Internal Server Error / System Failure.</p>
</header>
<hr>
<section id="message">
<h2>./error_message</h2>
<p>Something went wrong on our end. We are working to fix it.</p>
<br>
<p>Check the <a href="https://status.forust.xyz">System Status</a> for more information.</p>
</section>
<footer>
<p>root@error:~$ systemctl status service</p>
<p>Active: failed (Result: core-dump)</p>
<p>&copy; XRock - Just Signal.</p>
</footer>
</div>
</body>
</html>
+207
View File
@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>502 // Bad Gateway</title>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* hidden in a plain sight? */
:root {
--bg-color: #050505;
--text-color: #e0e0e0;
--accent: #ffffff;
--dim: #666666;
--font-mono: 'Courier New', Courier, monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-mono);
line-height: 1.6;
font-size: 16px;
padding: 2rem;
}
a {
color: var(--text-color);
text-decoration: none;
border-bottom: 1px solid var(--dim);
transition: all 0.2s;
}
a:hover {
background-color: var(--text-color);
color: var(--bg-color);
border-color: var(--text-color);
}
.container {
max-width: 800px;
margin: 0 auto;
}
/* TEXT */
h1 {
font-size: 2.5rem;
text-transform: uppercase;
letter-spacing: -2px;
margin-bottom: 0.5rem;
}
h2 {
font-size: 1.2rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--dim);
display: inline-block;
padding-right: 20px;
}
.subtitle {
color: var(--dim);
margin-bottom: 2rem;
}
hr {
border: 0;
border-top: 1px dashed var(--dim);
margin: 2rem 0;
}
.comment {
color: var(--dim);
font-size: 0.9rem;
margin-left: 10px;
}
/* SECTIONS */
section {
margin-bottom: 3rem;
}
/* LISTS */
ul {
list-style: none;
}
.link-list li {
margin-bottom: 0.8rem;
display: flex;
align-items: center;
gap: 15px;
}
/* STACK GRID */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.skill-item {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.level {
font-weight: bold;
}
.special .level {
color: var(--text-color);
text-shadow: 1px 0 0 red, -1px 0 0 blue;
}
/* my dudes */
.team-grid {
display: flex;
gap: 2rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.member {
text-align: center;
width: 100px;
}
.avatar {
width: 80px;
height: 80px;
background-color: #222;
border: 2px solid var(--text-color);
margin: 0 auto 10px auto;
background-size: cover;
}
/* if no avatar added: */
.placeholder::before {
content: "?";
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 2rem;
color: var(--dim);
}
/* REPOS */
.repo-list li {
margin-bottom: 1rem;
}
/* FOOTER */
footer {
text-align: center;
color: var(--dim);
font-size: 0.8rem;
/* flag{why-are-you-here?} */
margin-top: 4rem;
}
/* SMTH RESPONSIVE */
@media (max-width: 600px) {
.grid-2 {
grid-template-columns: 1fr;
gap: 0;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1 class="glitch" data-text="502">502</h1>
<p class="subtitle">> Bad Gateway / System Failure.</p>
</header>
<hr>
<section id="message">
<h2>./error_message</h2>
<p>The server received an invalid response from the upstream server.</p>
<br>
<p>Check the <a href="https://status.forust.xyz">System Status</a> for more information.</p>
</section>
<footer>
<p>root@error:~$ curl -I upstream_host</p>
<p>HTTP/1.1 502 Bad Gateway</p>
<p>&copy; XRock - Just Signal.</p>
</footer>
</div>
</body>
</html>
+207
View File
@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>503 // Service Unavailable</title>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* hidden in a plain sight? */
:root {
--bg-color: #050505;
--text-color: #e0e0e0;
--accent: #ffffff;
--dim: #666666;
--font-mono: 'Courier New', Courier, monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-mono);
line-height: 1.6;
font-size: 16px;
padding: 2rem;
}
a {
color: var(--text-color);
text-decoration: none;
border-bottom: 1px solid var(--dim);
transition: all 0.2s;
}
a:hover {
background-color: var(--text-color);
color: var(--bg-color);
border-color: var(--text-color);
}
.container {
max-width: 800px;
margin: 0 auto;
}
/* TEXT */
h1 {
font-size: 2.5rem;
text-transform: uppercase;
letter-spacing: -2px;
margin-bottom: 0.5rem;
}
h2 {
font-size: 1.2rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--dim);
display: inline-block;
padding-right: 20px;
}
.subtitle {
color: var(--dim);
margin-bottom: 2rem;
}
hr {
border: 0;
border-top: 1px dashed var(--dim);
margin: 2rem 0;
}
.comment {
color: var(--dim);
font-size: 0.9rem;
margin-left: 10px;
}
/* SECTIONS */
section {
margin-bottom: 3rem;
}
/* LISTS */
ul {
list-style: none;
}
.link-list li {
margin-bottom: 0.8rem;
display: flex;
align-items: center;
gap: 15px;
}
/* STACK GRID */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.skill-item {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.level {
font-weight: bold;
}
.special .level {
color: var(--text-color);
text-shadow: 1px 0 0 red, -1px 0 0 blue;
}
/* my dudes */
.team-grid {
display: flex;
gap: 2rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.member {
text-align: center;
width: 100px;
}
.avatar {
width: 80px;
height: 80px;
background-color: #222;
border: 2px solid var(--text-color);
margin: 0 auto 10px auto;
background-size: cover;
}
/* if no avatar added: */
.placeholder::before {
content: "?";
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 2rem;
color: var(--dim);
}
/* REPOS */
.repo-list li {
margin-bottom: 1rem;
}
/* FOOTER */
footer {
text-align: center;
color: var(--dim);
font-size: 0.8rem;
/* flag{why-are-you-here?} */
margin-top: 4rem;
}
/* SMTH RESPONSIVE */
@media (max-width: 600px) {
.grid-2 {
grid-template-columns: 1fr;
gap: 0;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1 class="glitch" data-text="503">503</h1>
<p class="subtitle">> Service Unavailable / System Failure.</p>
</header>
<hr>
<section id="message">
<h2>./error_message</h2>
<p>The server is currently unable to handle the request due to maintenance or overload.</p>
<br>
<p>Check the <a href="https://status.forust.xyz">System Status</a> for more information.</p>
</section>
<footer>
<p>root@error:~$ systemctl start service</p>
<p>Job for service failed because the control process exited with error code.</p>
<p>&copy; XRock - Just Signal.</p>
</footer>
</div>
</body>
</html>
+207
View File
@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>504 // Gateway Timeout</title>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* hidden in a plain sight? */
:root {
--bg-color: #050505;
--text-color: #e0e0e0;
--accent: #ffffff;
--dim: #666666;
--font-mono: 'Courier New', Courier, monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-mono);
line-height: 1.6;
font-size: 16px;
padding: 2rem;
}
a {
color: var(--text-color);
text-decoration: none;
border-bottom: 1px solid var(--dim);
transition: all 0.2s;
}
a:hover {
background-color: var(--text-color);
color: var(--bg-color);
border-color: var(--text-color);
}
.container {
max-width: 800px;
margin: 0 auto;
}
/* TEXT */
h1 {
font-size: 2.5rem;
text-transform: uppercase;
letter-spacing: -2px;
margin-bottom: 0.5rem;
}
h2 {
font-size: 1.2rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--dim);
display: inline-block;
padding-right: 20px;
}
.subtitle {
color: var(--dim);
margin-bottom: 2rem;
}
hr {
border: 0;
border-top: 1px dashed var(--dim);
margin: 2rem 0;
}
.comment {
color: var(--dim);
font-size: 0.9rem;
margin-left: 10px;
}
/* SECTIONS */
section {
margin-bottom: 3rem;
}
/* LISTS */
ul {
list-style: none;
}
.link-list li {
margin-bottom: 0.8rem;
display: flex;
align-items: center;
gap: 15px;
}
/* STACK GRID */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.skill-item {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.level {
font-weight: bold;
}
.special .level {
color: var(--text-color);
text-shadow: 1px 0 0 red, -1px 0 0 blue;
}
/* my dudes */
.team-grid {
display: flex;
gap: 2rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.member {
text-align: center;
width: 100px;
}
.avatar {
width: 80px;
height: 80px;
background-color: #222;
border: 2px solid var(--text-color);
margin: 0 auto 10px auto;
background-size: cover;
}
/* if no avatar added: */
.placeholder::before {
content: "?";
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 2rem;
color: var(--dim);
}
/* REPOS */
.repo-list li {
margin-bottom: 1rem;
}
/* FOOTER */
footer {
text-align: center;
color: var(--dim);
font-size: 0.8rem;
/* flag{why-are-you-here?} */
margin-top: 4rem;
}
/* SMTH RESPONSIVE */
@media (max-width: 600px) {
.grid-2 {
grid-template-columns: 1fr;
gap: 0;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1 class="glitch" data-text="504">504</h1>
<p class="subtitle">> Gateway Timeout / System Failure.</p>
</header>
<hr>
<section id="message">
<h2>./error_message</h2>
<p>The server did not receive a timely response from the upstream server.</p>
<br>
<p>Check the <a href="https://status.forust.xyz">System Status</a> for more information.</p>
</section>
<footer>
<p>root@error:~$ timeout 30s curl upstream</p>
<p>curl: (28) Operation timed out after 30001 milliseconds with 0 bytes received</p>
<p>&copy; XRock - Just Signal.</p>
</footer>
</div>
</body>
</html>
-1
View File
@@ -1,7 +1,6 @@
GITEA_POSTGRES_USER=
GITEA_POSTGRES_PASSWORD=
GITEA_POSTGRES_DB=gitea
BASIC-RUNNER_TOKEN=
GITEA_SMTP_PASS=
MAILER_ADDR=
SERVICE_EMAIL=email.used.by.services@domain.tld
+10 -39
View File
@@ -2,6 +2,7 @@ services:
server:
image: docker.gitea.com/gitea:1.25.1
container_name: gitea
restart: always
environment:
- USER_UID=1000
- USER_GID=1000
@@ -24,66 +25,37 @@ services:
- GITEA__mailer__PROTOCOL=SMTP
- GITEA__service__REGISTER_EMAIL_CONFIRM=true
- GITEA__service__ENABLE_NOTIFY_MAIL=true
restart: always
networks:
- gitea-db
- proxy
volumes:
- ./gitea-data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
# Prod Router
- "traefik.http.routers.gitea.rule=Host(`gitea.forust.xyz`)"
- "traefik.http.routers.gitea.entrypoints=websecure"
- "traefik.http.routers.gitea.middlewares=security-headers@file"
- "traefik.http.routers.gitea.service=gitea"
- "traefik.http.routers.gitea.tls=true"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
# Local Router
- "traefik.http.routers.gitea-local.rule=Host(`gitea.workstation.internal`) || Host(`gitea.internal`)"
- "traefik.http.routers.gitea-local.rule=Host(`gitea.workstation.internal`)"
- "traefik.http.routers.gitea-local.entrypoints=websecure"
- "traefik.http.routers.gitea-local.middlewares=security-headers@file"
- "traefik.http.routers.gitea-local.service=gitea"
- "traefik.http.routers.gitea-local.tls=true"
# Dev Router
- "traefik.http.routers.gitea-dev.rule=Host(`gitea.gigaforust.internal`)"
- "traefik.http.routers.gitea-dev.entrypoints=websecure"
- "traefik.http.routers.gitea-dev.middlewares=security-headers@file"
- "traefik.http.routers.gitea-dev.service=gitea"
- "traefik.http.routers.gitea-dev.tls=true"
# SSH Router
- "traefik.tcp.services.gitea.loadbalancer.server.port=22"
- "traefik.tcp.routers.gitea.entrypoints=ssh"
- "traefik.tcp.routers.gitea.rule=HostSNI(`*`)"
- "traefik.tcp.services.gitea.loadbalancer.server.port=22"
ports:
- "2221:22"
depends_on:
- db
runner:
image: gitea/act_runner:0.2.11
container_name: gitea-runner
restart: always
depends_on:
- server
env_file:
- .env
networks:
- gitea-db
environment:
- GITEA_INSTANCE_URL=http://server:3000
- GITEA_RUNNER_REGISTRATION_TOKEN=${BASIC-RUNNER_TOKEN}
- GITEA_RUNNER_NAME=basic-runner
- GITEA_RUNNER_LABELS=docker:docker://node:20-bookworm,ubuntu-latest:docker://node:20-bookworm
volumes:
- ./gitea-runner:/data
- /var/run/docker.sock:/var/run/docker.sock
- proxy
depends_on:
- db
db:
image: docker.io/library/postgres:14
restart: always
@@ -91,11 +63,10 @@ services:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=gitea
- POSTGRES_DB=gitea
networks:
- gitea-db
volumes:
- ./gitea-db/:/var/lib/postgresql/data
networks:
- gitea-db
networks:
gitea-db:
external: false
+2 -9
View File
@@ -12,29 +12,22 @@ services:
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.services.glance.loadbalancer.server.port=8080"
# Prod Router
- "traefik.http.routers.glance.rule=Host(`glance.forust.xyz`)"
- "traefik.http.routers.glance.entrypoints=websecure"
- "traefik.http.routers.glance.middlewares=security-chain@file"
- "traefik.http.routers.glance.tls=true"
# Local Router
- "traefik.http.routers.glance-local.rule=Host(`glance.workstation.internal`) || Host(`glance.internal`)"
- "traefik.http.routers.glance-local.rule=Host(`glance.workstation.internal`)"
- "traefik.http.routers.glance-local.entrypoints=websecure"
- "traefik.http.routers.glance-local.middlewares=security-headers@file"
- "traefik.http.routers.glance-local.tls=true"
# Dev Router
- "traefik.http.routers.glance-dev.rule=Host(`glance.gigaforust.internal`)"
- "traefik.http.routers.glance-dev.entrypoints=websecure"
- "traefik.http.routers.glance-dev.middlewares=security-chain@file"
- "traefik.http.routers.glance-dev.tls=true"
networks:
- proxy
dns:
- 1.1.1.1
- 8.8.8.8
networks:
proxy:
external: true
+93
View File
@@ -0,0 +1,93 @@
services:
headscale:
image: headscale/headscale:latest
restart: unless-stopped
container_name: headscale-server
command: serve
networks:
- proxy
volumes:
- ./config/headscale.yaml:/etc/headscale/config.yaml
- data:/var/lib/headscale
- ./config/policy.json:/var/lib/headscale/policy.json
labels:
- "me.tale.headplane.target: headscale"
- "traefik.enable=true"
- "traefik.http.services.headscale.loadbalancer.server.port=8080"
- "traefik.http.services.headscale-metrics.loadbalancer.server.port=9090"
## SERVICE
# Prod Router
- "traefik.http.routers.headscale.rule=Host(`hs.forust.xyz`)"
- "traefik.http.routers.headscale.entrypoints=websecure"
- "traefik.http.routers.headscale.service=headscale"
- "traefik.http.routers.headscale.tls=true"
# Local Router
- "traefik.http.routers.headscale-local.rule=Host(`hs.workstation.internal`)"
- "traefik.http.routers.headscale-local.entrypoints=websecure"
- "traefik.http.routers.headscale-local.service=headscale"
- "traefik.http.routers.headscale-local.tls=true"
# Dev Router
- "traefik.http.routers.headscale-dev.rule=Host(`hs.gigaforust.internal`)"
- "traefik.http.routers.headscale-dev.entrypoints=websecure"
- "traefik.http.routers.headscale-dev.service=headscale"
- "traefik.http.routers.headscale-dev.tls=true"
## METRICS
# Prod Router
- "traefik.http.routers.headscale-metrics.rule=Host(`hs.forust.xyz`) && PathPrefix(`/metrics`)"
- "traefik.http.routers.headscale-metrics.entrypoints=websecure"
- "traefik.http.routers.headscale-metrics.service=headscale-metrics"
- "traefik.http.routers.headscale-metrics.tls=true"
# Local Router
- "traefik.http.routers.headscale-metrics-local.rule=Host(`hs.workstation.internal`) && PathPrefix(`/metrics`)"
- "traefik.http.routers.headscale-metrics-local.entrypoints=websecure"
- "traefik.http.routers.headscale-metrics-local.service=headscale-metrics"
- "traefik.http.routers.headscale-metrics-local.tls=true"
# Dev Router
- "traefik.http.routers.headscale-metrics-dev.rule=Host(`hs.gigaforust.internal`) && PathPrefix(`/metrics`)"
- "traefik.http.routers.headscale-metrics-dev.entrypoints=websecure"
- "traefik.http.routers.headscale-metrics-dev.service=headscale-metrics"
- "traefik.http.routers.headscale-metrics-dev.tls=true"
headplane:
image: ghcr.io/tale/headplane:latest
container_name: headplane
restart: unless-stopped
ports:
- '3000:3000'
volumes:
- ./config/headplane.yaml:/etc/headplane/config.yaml
- ./config/headscale.yaml:/etc/headscale/config.yaml
- headplane-data:/var/lib/headplane
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy
web:
image: goodieshq/headscale-admin:latest
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.services.headscale-ui.loadbalancer.server.port=80"
# Prod Router
- "traefik.http.routers.headscale-ui.rule=Host(`hs.forust.xyz`) && PathPrefix(`/admin`)"
- "traefik.http.routers.headscale-ui.entrypoints=websecure"
- "traefik.http.routers.headscale-ui.middlewares=security-chain@file"
- "traefik.http.routers.headscale-ui.tls=true"
# Local Router
- "traefik.http.routers.headscale-ui-local.rule=Host(`hs.workstation.internal`) && PathPrefix(`/admin`)"
- "traefik.http.routers.headscale-ui-local.entrypoints=websecure"
- "traefik.http.routers.headscale-ui-local.tls=true"
# Dev Router
- "traefik.http.routers.headscale-ui-dev.rule=Host(`hs.gigaforust.internal`) && PathPrefix(`/admin`)"
- "traefik.http.routers.headscale-ui-dev.entrypoints=websecure"
- "traefik.http.routers.headscale-ui-dev.tls=true"
networks:
- proxy
volumes:
data:
headplane-data:
name: headplane_data
networks:
proxy:
external: true
+221
View File
@@ -0,0 +1,221 @@
# Configuration for the Headplane server and web application
server:
# These are the default values, change them as needed
host: "0.0.0.0"
port: 3000
# Should not include the dashboard prefix (/admin) portion.
# # Prod server_url
# base_url: https://hs.forust.xyz
# # Local base_url
# base_url: https://hs.workstation.internal
# # Dev base_url
# base_url: https://hs.gigaforust.internal
# You may provide `cookie_secret_path` instead to read a value from disk.
# See https://headplane.net/configuration/#sensitive-values
cookie_secret: "<change_me_to_something_secure!>"
# Whether cookies should be marked as Secure
# * Should be false if running without HTTPs
# * Should be true if running behind a reverse proxy with HTTPs
cookie_secure: true
# The maximum age of the session cookie in seconds
cookie_max_age: 86400 # 1 day in seconds
# This is not required, but if you want to restrict the cookie
# to a specific domain, set it here. Otherwise leave it commented out.
# This may not work as expected if not using a reverse proxy.
# cookie_domain: ""
# The path to persist Headplane specific data. All data going forward
# is stored in this directory, including the internal database and
# any cache related files.
data_path: "/var/lib/headplane"
# The info secret is optional and allows access to certain debug endpoints
# that may expose sensitive information about your Headplane instance.
#
# As of now, this protects the /api/info endpoint which exposes details about
# the Headplane and Headscale versions in use. In the future, more endpoints
# may be protected by this secret.
#
# If not set, these endpoints will be disabled.
# info_secret: "<change_me_to_something_secure!>"
# Headscale specific settings to allow Headplane to talk
# to Headscale and access deep integration features
headscale:
# The URL to your Headscale instance
# (All API requests are routed through this URL)
# (THIS IS NOT the gRPC endpoint, but the HTTP endpoint)
#
# IMPORTANT: If you are using TLS this MUST be set to `https://`
url: "http://headscale-server:8080"
# If you use the TLS configuration in Headscale, and you are not using
# Let's Encrypt for your certificate, pass in the path to the certificate.
# (This has no effect if `url` does not start with `https://`)
# tls_cert_path: "/var/lib/headplane/tls.crt"
# Optional, public URL if its different from the `headscale.url`
# This affects certain parts of the web UI which shows Headscale's URL
public_url: "https://headscale.example.com"
# Path to the Headscale configuration file
# This is optional, but HIGHLY recommended for the best experience
# If this is read only, Headplane will show your configuration settings
# in the Web UI, but they cannot be changed.
config_path: "/etc/headscale/config.yaml"
# Whether the Headscale configuration should be strictly validated
# when reading from `config_path`. If true, Headplane will not interact
# with Headscale if there are any issues with the configuration file.
#
# This is recommended to be true for production deployments to, however it
# may not work if you are using a version of Headscale that has configuration
# options unknown to Headplane.
config_strict: true
# If you are using `dns.extra_records_path` in your Headscale
# configuration, you need to set this to the path for Headplane
# to be able to read the DNS records.
#
# Pass it in if using Docker and ensure that the file is both
# readable and writable to the Headplane process.
# When using this, Headplane will no longer need to automatically
# restart Headscale for DNS record changes.
# dns_records_path: "/var/lib/headscale/extra_records.json"
# Integration configurations for Headplane to interact with Headscale
integration:
# The Headplane agent allows retrieving information about nodes
# This allows the UI to display version, OS, and connectivity data
# You will see the Headplane agent in your Tailnet as a node when
# it connects.
agent:
enabled: false
# To connect to your Tailnet, you need to generate a pre-auth key
# This can be done via the web UI or through the `headscale` CLI.
pre_authkey: "<your-preauth-key>"
# Optionally change the name of the agent in the Tailnet.
# host_name: "headplane-agent"
# Configure different caching settings. By default, the agent will store
# caches in the path below for a maximum of 1 minute. If you want data
# to update faster, reduce the TTL, but this will increase the frequency
# of requests to Headscale.
# cache_ttl: 60
# cache_path: /var/lib/headplane/agent_cache.json
# The work_dir represents where the agent will store its data to be able
# to automatically reauthenticate with your Tailnet. It needs to be
# writable by the user running the Headplane process.
#
# If using Docker, it is best to leave this as the default.
# work_dir: "/var/lib/headplane/agent"
# Only one of these should be enabled at a time or you will get errors
# This does not include the agent integration (above), which can be enabled
# at the same time as any of these and is recommended for the best experience.
docker:
enabled: true
# By default we check for the presence of a container label (see the docs)
# to determine the container to signal when changes are made to DNS settings.
container_label: "me.tale.headplane.target=headscale"
# HOWEVER, you can fallback to a container name if you desire, but this is
# not recommended as its brittle and doesn't work with orchestrators that
# automatically assign container names.
#
# If `container_name` is set, it will override any label checks.
# container_name: "headscale-server"
# The path to the Docker socket (do not change this if you are unsure)
# Docker socket paths must start with unix:// or tcp:// and at the moment
# https connections are not supported.
socket: "unix:///var/run/docker.sock"
# Please refer to docs/integration/Kubernetes.md for more information
# on how to configure the Kubernetes integration. There are requirements in
# order to allow Headscale to be controlled by Headplane in a cluster.
kubernetes:
enabled: false
# Validates the manifest for the Pod to ensure all of the criteria
# are set correctly. Turn this off if you are having issues with
# shareProcessNamespace not being validated correctly.
validate_manifest: true
# This should be the name of the Pod running Headscale and Headplane.
# If this isn't static you should be using the Kubernetes Downward API
# to set this value (refer to docs/Integrated-Mode.md for more info).
pod_name: "headscale"
# Proc is the "Native" integration that only works when Headscale and
# Headplane are running outside of a container. There is no configuration,
# but you need to ensure that the Headplane process can terminate the
# Headscale process.
#
# (If they are both running under systemd as sudo, this will work).
proc:
enabled: false
# OIDC Configuration for simpler authentication
# (This is optional, but recommended for the best experience)
# oidc:
# The OIDC issuer URL
# issuer: "https://accounts.google.com"
# If you are using OIDC, you need to generate an API key
# that can be used to authenticate other sessions when signing in.
#
# This can be done with `headscale apikeys create --expiration 999d`
# headscale_api_key: "<your-headscale-api-key>"
# If your OIDC provider does not support discovery (does not have the URL at
# `/.well-known/openid-configuration`), you need to manually set endpoints.
# This also works to override endpoints if you so desire or if your OIDC
# discovery is missing certain endpoints (ie GitHub).
# For some typical providers, see https://headplane.net/features/sso.
# authorization_endpoint: ""
# token_endpoint: ""
# userinfo_endpoint: ""
# The authentication method to use when communicating with the token endpoint.
# This is fully optional and Headplane will attempt to auto-detect the best
# method and fall back to `client_secret_basic` if unsure.
# token_endpoint_auth_method: "client_secret_post"
# The client ID for the OIDC client
# For the best experience please ensure this is *identical* to the client_id
# you are using for Headscale. because
# client_id: "your-client-id"
# The client secret for the OIDC client
# You may also provide `client_secret_path` instead to read a value from disk.
# See https://headplane.net/configuration/#sensitive-values
# client_secret: "<your-client-secret>"
# Whether to use PKCE when authenticating users. This is recommended as it
# adds an extra layer of security to the authentication process. Enabling this
# means your OIDC provider must support PKCE and it must be enabled on the
# client.
# use_pkce: true
# If you want to disable traditional login via Headscale API keys
# disable_api_key_login: false
# By default profile pictures are pulled from the OIDC provider when
# we go to fetch the userinfo endpoint. Optionally, this can be set to
# "oidc" or "gravatar" as of 0.6.1.
# profile_picture_source: "gravatar"
# The scopes to request when authenticating users. The default is below.
# scope: "openid email profile"
# Extra query parameters can be passed to the authorization endpoint
# by setting them here. This is useful for providers that require any kind
# of custom hinting.
# extra_params:
# prompt: "select_account" # Example: force account selection on Google
+79
View File
@@ -0,0 +1,79 @@
# https://wiki.serversatho.me/en/headscale
# # Prod server_url
# server_url: https://hs.example.com
# # Local server_url
# server_url: https://hs.internal_domain.internal
# # Dev server_url
# server_url: https://hs.dev_internal_domain.internal
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 127.0.0.1:50443
grpc_allow_insecure: false
noise:
private_key_path: /var/lib/headscale/noise_private.key
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
allocation: sequential
derp:
server:
enabled: true
region_id: 999
region_code: "headscale"
region_name: "Headscale Embedded DERP"
stun_listen_addr: "0.0.0.0:3478"
private_key_path: /var/lib/headscale/derp_server_private.key
automatically_add_embedded_derp_region: true
ipv4: 1.2.3.4
ipv6: 2001:db8::1
urls:
- https://controlplane.tailscale.com/derpmap/default
paths: []
auto_update_enabled: true
update_frequency: 24h
disable_check_updates: false
ephemeral_node_inactivity_timeout: 30m
database:
type: sqlite
debug: false
gorm:
prepare_stmt: true
parameterized_queries: true
skip_err_record_not_found: true
slow_threshold: 1000
sqlite:
path: /var/lib/headscale/db.sqlite
write_ahead_log: true
wal_autocheckpoint: 1000
acme_url: https://acme-v02.api.letsencrypt.org/directory
acme_email: ""
tls_letsencrypt_hostname: ""
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
tls_letsencrypt_challenge_type: HTTP-01
tls_letsencrypt_listen: ":http"
tls_cert_path: ""
tls_key_path: ""
log:
format: text
level: info
policy:
mode: database
path: ""
dns:
magic_dns: true
base_domain: example.com
nameservers:
global:
- 1.1.1.1
- 1.0.0.1
- 2606:4700:4700::1111
- 2606:4700:4700::1001
split: {}
search_domains: []
extra_records: []
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
logtail:
enabled: false
randomize_client_port: false
+26
View File
@@ -0,0 +1,26 @@
{
"groups": {
"group:admin": [
"admin@"
],
"group:users": []
},
"tagOwners": {},
"hosts": {},
"acls": [
{
"#ha-meta": {
"name": "users",
"open": true
},
"action": "accept",
"src": [
"autogroup:member"
],
"dst": [
"autogroup:self:*"
]
}
],
"ssh": []
}
+9 -33
View File
@@ -3,8 +3,8 @@ services:
build:
context: .
dockerfile: Dockerfile.forust
ports:
- "8085:80"
# ports:
# - "8085:80"
restart: unless-stopped
volumes:
- ./forust_files:/usr/share/nginx/html
@@ -12,72 +12,48 @@ services:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# Services
- "traefik.http.services.forust-homepage.loadbalancer.server.port=80"
# Prod Router
- "traefik.http.routers.forust-homepage.rule=Host(`forust.xyz`)"
- "traefik.http.routers.forust-homepage.entrypoints=websecure"
- "traefik.http.routers.forust-homepage.middlewares=security-headers@file"
- "traefik.http.routers.forust-homepage.service=forust-homepage"
- "traefik.http.routers.forust-homepage.tls=true"
# Local Router
- "traefik.http.routers.forust-homepage-local.rule=Host(`landing.workstation.internal`) || Host(`landing.internal`)"
- "traefik.http.routers.forust-homepage-local.rule=Host(`landing.workstation.internal`)"
- "traefik.http.routers.forust-homepage-local.entrypoints=websecure"
- "traefik.http.routers.forust-homepage-local.middlewares=security-headers@file"
- "traefik.http.routers.forust-homepage-local.service=forust-homepage"
- "traefik.http.routers.forust-homepage-local.tls=true"
# Dev Router
- "traefik.http.routers.forust-homepage-dev.rule=Host(`landing.gigaforust.internal`)"
- "traefik.http.routers.forust-homepage-dev.entrypoints=websecure"
- "traefik.http.routers.forust-homepage-dev.middlewares=security-headers@file"
- "traefik.http.routers.forust-homepage-dev.service=forust-homepage"
- "traefik.http.routers.forust-homepage-dev.tls=true"
xdfnx:
build:
context: .
dockerfile: Dockerfile.xdfnx
ports:
- "8086:80"
restart: unless-stopped
# ports:
# - "8086:80"
volumes:
- ./xdfnx_files:/usr/share/nginx/html
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# Services
- "traefik.http.services.xdfnx-homepage.loadbalancer.server.port=80"
# Prod Router
- "traefik.http.routers.xdfnx.rule=Host(`xdfnx.cfd`)"
- "traefik.http.routers.xdfnx.entrypoints=websecure"
- "traefik.http.routers.xdfnx.middlewares=security-headers@file"
- "traefik.http.routers.xdfnx.service=xdfnx-homepage"
- "traefik.http.routers.xdfnx.tls.certresolver=letsencrypt"
- "traefik.http.routers.xdfnx.tls=true"
# Local Router
- "traefik.http.routers.xdfnx-local.rule=Host(`xdfnx.workstation.internal`) || Host(`xdfnx.internal`)"
- "traefik.http.routers.xdfnx-local.rule=Host(`xdfnx.workstation.internal`)"
- "traefik.http.routers.xdfnx-local.entrypoints=websecure"
- "traefik.http.routers.xdfnx-local.middlewares=security-headers@file"
- "traefik.http.routers.xdfnx-local.service=xdfnx-homepage"
- "traefik.http.routers.xdfnx-local.tls=true"
# Dev Router
- "traefik.http.routers.xdfnx-dev.rule=Host(`xdfnx.gigaforust.internal`)"
- "traefik.http.routers.xdfnx-dev.entrypoints=websecure"
- "traefik.http.routers.xdfnx-dev.middlewares=security-headers@file"
- "traefik.http.routers.xdfnx-dev.service=xdfnx-homepage"
- "traefik.http.routers.xdfnx-dev.tls=true"
networks:
- proxy
networks:
proxy:
external: true
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

+9 -2
View File
@@ -41,7 +41,14 @@
</li>
<li>
<i class="fas fa-envelope"></i>
<a href="mailto:forust@forust.xyz">forust@forust.xyz</a>
<a href="mailto:contact@forust.xyz">mail/contact@forust.xyz</a>
</li>
<li>
<i class="fa-solid fa-key"></i>
<a href=".well-known/pgp-key.asc">security/PGP Key</a>
</li>
<li>
<p class="comment"># PGP Key Fingerprint: A777 7CB7 D9C4 0A97 443D CCF0 7A3D A455 F820 5B82</p>
</li>
</ul>
</section>
@@ -109,7 +116,7 @@
<div class="avatar"
style="background-image: url('assets/images/team/anna.jpg'); background-size: cover; background-position: center;">
</div>
<a href="" target="_blank">Anna~</a>
<a href="./assets/images/love.png" target="_blank">Anna~</a>
</div>
<div class="member">
+54
View File
@@ -0,0 +1,54 @@
# Resolved: Overlay FS failure (and so containers)
15-12-2025 03:02 AM EET: Degraded control panels' performances, following by full cascade docker failure
15-12-2025 04:36 AM EET: Identified: Services are terminated due to server software (Overlay FS) + hardware issues (HDD).
15-12-2025 08:45 PM EET: Restored NextCloud service with few tweaks to lower I/O
---
16-12-2025 08:34 PM EET: Ordered new HDD, ETA 22nd of December - 2nd of January
---
17-12-2025 02:47 AM EET: To avoid additional I/O into kuma's database, disabled uptime monitoring for non-critical services, such as:
- Game servers
- Gitea (no public projects being hosted yet)
- Landings
- Cloud services
- PenPot
- Auth provider
- Secondary management tools
- Chernuha's non-important infrastructure
These can be identified by seeing ">2m ago" under monitor's heartbeats.
---
09-01-2026 02:32 PM EET: NextCloud's frontend files are corrupted due to the unknown issue. All user data is integrity-verified. To prevent user data corruption, NextCloud service will be restored after new hardware will be available.
---
14-01-2026 08:42 PM EET: After planned updating and restarting server, critical firmware software were corrupted because of physical degradation of the disk. Server inaccessible in any way
---
15-01-2026 11:30 AM EET: A new NAS-Grade HDD (Seagate IronWolf Pro) was ordered. ETA 16-01-2026 EET Before 12:00 PM
---
16-01-2026 12:47 AM EET: New server system is installed, data backed up. Experiencing docker memory leak.
---
17-01-2026 01:27 PM EET: All services except NextCloud and Satisfactory server are online.
17-01-2026 03:48 PM EET: Nextcloud is online. Satisfactory will be provided on-demand. Monitoring status
---
##### Status: All services are online
**Solution: moving all infrastructure onto new NAS-Grade HDD with fresh OS install**
+3
View File
@@ -0,0 +1,3 @@
KENER_SECRET_KEY=your_secret_key_here
ORIGIN=http://localhost:3000
TZ=Etc/UTC
+59
View File
@@ -0,0 +1,59 @@
services:
kener:
image: rajnandan1/kener:v4.0.23
container_name: kener
restart: unless-stopped
# ports:
# - 3000:3000/tcp
environment:
- KENER_SECRET_KEY=${KENER_SECRET_KEY?Kener requires a secret key}
- ORIGIN=${ORIGIN:-http://localhost:3000}
- REDIS_URL=redis://redis:6379
- TZ:${TZ:-Etc/UTC}
depends_on:
redis:
condition: service_healthy
volumes:
- db:/app/database
- uploads:/app/uploads
labels:
- "traefik.enable=true"
- "traefik.http.services.kener.loadbalancer.server.port=3000"
# Prod Router
- "traefik.http.routers.kener.rule=Host(`status.forust.xyz`)"
- "traefik.http.routers.kener.entrypoints=websecure"
- "traefik.http.routers.kener.tls=true"
# Local Router
- "traefik.http.routers.kener-local.rule=Host(`status.workstation.internal`)"
- "traefik.http.routers.kener-local.entrypoints=websecure"
- "traefik.http.routers.kener-local.tls=true"
# Dev Router
- "traefik.http.routers.kener-dev.rule=Host(`status.gigaforust.internal`)"
- "traefik.http.routers.kener-dev.entrypoints=websecure"
- "traefik.http.routers.kener-dev.tls=true"
networks:
- proxy
- kener
redis:
image: redis:7-alpine
container_name: kener-redis
restart: unless-stopped
volumes:
- redis:/data
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
interval: 6s
timeout: 5s
retries: 5
networks:
- kener
volumes:
db:
uploads:
redis:
networks:
proxy:
external: true
kener:
external: false
+4 -13
View File
@@ -1,7 +1,7 @@
services:
metube:
image: ghcr.io/alexta69/metube
# container_name: metube
container_name: metube
restart: unless-stopped
# ports:
# - "8081:8081"
@@ -13,31 +13,22 @@ services:
volumes:
- ./MeTube_downloads:/downloads
labels:
- traefik.enable=true
- "traefik.docker.network=proxy"
- "traefik.enable=true"
- "traefik.http.services.metube.loadbalancer.server.port=8081"
# Prod Router
- "traefik.http.routers.metube.rule=Host(`metube.forust.xyz`)"
- "traefik.http.routers.metube.entrypoints=websecure"
- "traefik.http.routers.metube.middlewares=security-chain@file"
- "traefik.http.routers.metube.service=metube"
- "traefik.http.routers.metube.tls=true"
- "traefik.http.services.metube.loadbalancer.server.port=8081"
# Local Router
- "traefik.http.routers.metube-local.rule=Host(`metube.workstation.internal`) || Host(`metube.internal`)"
- "traefik.http.routers.metube-local.rule=Host(`metube.workstation.internal`)"
- "traefik.http.routers.metube-local.entrypoints=websecure"
- "traefik.http.routers.metube-local.middlewares=security-headers@file"
- "traefik.http.routers.metube-local.service=metube"
- "traefik.http.routers.metube-local.tls=true"
# Dev Router
- "traefik.http.routers.metube-dev.rule=Host(`metube.gigaforust.internal`)"
- "traefik.http.routers.metube-dev.entrypoints=websecure"
- "traefik.http.routers.metube-dev.middlewares=security-chain@file"
- "traefik.http.routers.metube-dev.service=metube"
- "traefik.http.routers.metube-dev.tls=true"
networks:
- proxy
networks:
+8 -13
View File
@@ -13,8 +13,8 @@ services:
- N8N_SECURE_COOKIE=false
- N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true
volumes:
- ./n8n-node-data:/home/node/.n8n
- ./n8n-files:/files
- node-data:/home/node/.n8n
- files:/files
extra_hosts:
- "enterprise.n8n.io:104.26.13.187"
- "enterprise.n8n.io:104.26.12.187"
@@ -27,28 +27,19 @@ services:
- n8n
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
# Prod Router
- "traefik.http.routers.n8n.rule=Host(`n8n.forust.xyz`)"
- "traefik.http.routers.n8n.entrypoints=websecure"
- "traefik.http.routers.n8n.middlewares=security-headers@file"
- "traefik.http.routers.n8n.service=n8n"
- "traefik.http.routers.n8n.tls=true"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
# Local Router
- "traefik.http.routers.n8n-local.rule=Host(`n8n.workstation.internal`) || Host(`n8n.internal`)"
- "traefik.http.routers.n8n-local.rule=Host(`n8n.workstation.internal`)"
- "traefik.http.routers.n8n-local.entrypoints=websecure"
- "traefik.http.routers.n8n-local.middlewares=security-headers@file"
- "traefik.http.routers.n8n-local.service=n8n"
- "traefik.http.routers.n8n-local.tls=true"
# Dev Router
- "traefik.http.routers.n8n-dev.rule=Host(`n8n.gigaforust.internal`)"
- "traefik.http.routers.n8n-dev.entrypoints=websecure"
- "traefik.http.routers.n8n-dev.middlewares=security-headers@file"
- "traefik.http.routers.n8n-dev.service=n8n"
- "traefik.http.routers.n8n-dev.tls=true"
- glance.name=n8n
@@ -60,3 +51,7 @@ networks:
external: false
proxy:
external: true
volumes:
node-data:
files:
+29
View File
@@ -0,0 +1,29 @@
## https://github.com/autobrr/netronome?tab=readme-ov-file#environment-variables
# Server settings
NETRONOME__HOST=0.0.0.0 # Listen address
NETRONOME__PORT=7575 # Web UI port
NETRONOME__BASE_URL=/netronome # Base URL for reverse proxy
# Database (SQLite by default)
NETRONOME__DB_TYPE=postgres # sqlite or postgres
NETRONOME__DB_PATH=netronome.db # SQLite database path
# PostgreSQL (when DB_TYPE=postgres)
NETRONOME__DB_TYPE=postgres
NETRONOME__DB_HOST=postgres
NETRONOME__DB_PORT=5432
NETRONOME__DB_USER=netronome
NETRONOME__DB_PASSWORD=netronome
NETRONOME__DB_NAME=netronome
NETRONOME__DB_SSLMODE=disable
# Authentication
NETRONOME__AUTH_WHITELIST=127.0.0.1/32,192.168.1.0/24 # IP whitelist (comma-separated)
NETRONOME__SESSION_SECRET= # Session secret (auto-generated if empty)
# OIDC (optional)
NETRONOME__OIDC_ISSUER=https://accounts.google.com
NETRONOME__OIDC_CLIENT_ID=your-client-id
NETRONOME__OIDC_CLIENT_SECRET=your-secret
NETRONOME__OIDC_REDIRECT_URL=https://example.com/api/auth/oidc/callback
+59
View File
@@ -0,0 +1,59 @@
services:
netronome:
image: ghcr.io/autobrr/netronome:latest
restart: unless-stopped
container_name: netronome
ports:
- "7575:7575"
cap_add:
- NET_RAW
env_file:
- .env
labels:
- "traefik.enable=true"
- "traefik.http.services.netronome.loadbalancer.server.port=7575"
# Prod Router
- "traefik.http.routers.netronome.rule=Host(`nm.forust.xyz`)"
- "traefik.http.routers.netronome.entrypoints=websecure"
- "traefik.http.routers.netronome.tls=true"
# Local Router
- "traefik.http.routers.netronome-local.rule=Host(`nm.workstation.internal`)"
- "traefik.http.routers.netronome-local.entrypoints=websecure"
- "traefik.http.routers.netronome-local.tls=true"
# Dev Router
- "traefik.http.routers.netronome-dev.rule=Host(`nm.gigaforust.internal`)"
- "traefik.http.routers.netronome-dev.entrypoints=websecure"
- "traefik.http.routers.netronome-dev.tls=true"
networks:
- proxy
- netronome
depends_on:
postgres:
condition: service_healthy
postgres:
container_name: netronome-postgres
image: postgres:17-alpine
environment:
- POSTGRES_USER=netronome
- POSTGRES_PASSWORD=netronome
- POSTGRES_DB=netronome
volumes:
- data:/var/lib/postgresql/data
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U netronome" ]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- netronome
volumes:
data:
networks:
proxy:
external: true
netronome:
external: false
+6 -17
View File
@@ -17,40 +17,29 @@ services:
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# AIO Services configuration
- "traefik.http.services.nextcloud-aio.loadbalancer.server.port=8080"
- "traefik.http.services.nextcloud-aio.loadbalancer.server.scheme=https"
- "traefik.http.services.nextcloud-aio.loadbalancer.serverstransport=insecureTransport@file"
# Prod Router
# - "traefik.http.routers.nextcloud-aio.rule=Host(`nextcloud-aio.forust.xyz`)"
# - "traefik.http.routers.nextcloud-aio.rule=Host(`naio.forust.xyz`)"
# - "traefik.http.routers.nextcloud-aio.entrypoints=websecure"
# - "traefik.http.routers.nextcloud-aio.middlewares=security-headers"
# - "traefik.http.routers.nextcloud-aio.service=nextcloud-aio"
# - "traefik.http.routers.nextcloud-aio.middlewares=security-chain@file"
# - "traefik.http.routers.nextcloud-aio.tls=true"
# Local Router
- "traefik.http.routers.nextcloud-aio-local.rule=Host(`nextcloud-aio.workstation.internal`) || Host(`nextcloud-aio.internal`)"
- "traefik.http.routers.nextcloud-aio-local.rule=Host(`naio.workstation.internal`)"
- "traefik.http.routers.nextcloud-aio-local.entrypoints=websecure"
- "traefik.http.routers.nextcloud-aio-local.middlewares=security-headers@file"
- "traefik.http.routers.nextcloud-aio-local.service=nextcloud-aio"
- "traefik.http.routers.nextcloud-aio-local.tls=true"
# Dev Router
- "traefik.http.routers.nextcloud-aio-dev.rule=Host(`nextcloud-aio.gigaforust.internal`)"
- "traefik.http.routers.nextcloud-aio-dev.rule=Host(`naio.gigaforust.internal`)"
- "traefik.http.routers.nextcloud-aio-dev.entrypoints=websecure"
- "traefik.http.routers.nextcloud-aio-dev.middlewares=security-headers@file"
- "traefik.http.routers.nextcloud-aio-dev.service=nextcloud-aio"
- "traefik.http.routers.nextcloud-aio-dev.tls=true"
# Glanceapp/glance config
# Glance Metadata
- glance.name=Nextcloud
# - glance.icon=si:nextcloud
- glance.url=https://nextcloud.forust.xyz/
- glance.description=Nextcloud is a suite of client-server software for creating and using file hosting services.
environment:
AIO_DISABLE_BACKUP_SECTION: false
APACHE_PORT: 11000 # Is needed when running behind a web server or reverse proxy (like Apache, Nginx, Caddy, Cloudflare Tunnel and else). See https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md
@@ -59,7 +48,7 @@ services:
BORG_RETENTION_POLICY: --keep-within=7d --keep-weekly=4 --keep-monthly=6 # Backup retention See https://github.com/nextcloud/all-in-one#how-to-adjust-borgs-retention-policy
COLLABORA_SECCOMP_DISABLED: false # Setting this to true allows to disable Collabora's Seccomp feature. See https://github.com/nextcloud/all-in-one#how-to-disable-collaboras-seccomp-feature
FULLTEXTSEARCH_JAVA_OPTIONS: "-Xms1024M -Xmx1024M" # adjust fulltextsearch java options. https://github.com/nextcloud/all-in-one#how-to-adjust-the-fulltextsearch-java-options
NEXTCLOUD_DATADIR: /media/forust/nextcloud/ncdata # Allows to set the host directory for Nextcloud's datadir. ⚠️⚠️⚠️ Warning: do not set or adjust this value after the initial Nextcloud installation is done! See https://github.com/nextcloud/all-in-one#how-to-change-the-default-location-of-nextclouds-datadir
NEXTCLOUD_DATADIR: /mnt/nextcloud/ncdata # Allows to set the host directory for Nextcloud's datadir. ⚠️⚠️⚠️ Warning: do not set or adjust this value after the initial Nextcloud installation is done! See https://github.com/nextcloud/all-in-one#how-to-change-the-default-location-of-nextclouds-datadir
NEXTCLOUD_MOUNT: /mnt/ # Allows the Nextcloud container to access the chosen directory on the host. See https://github.com/nextcloud/all-in-one#how-to-allow-the-nextcloud-container-to-access-directories-on-the-host
NEXTCLOUD_UPLOAD_LIMIT: 16G # https://github.com/nextcloud/all-in-one#how-to-adjust-the-upload-limit-for-nextcloud
NEXTCLOUD_MAX_TIME: 7200 # Max uploading time See https://github.com/nextcloud/all-in-one#how-to-adjust-the-max-execution-time-for-nextcloud
+4 -15
View File
@@ -86,8 +86,8 @@ services:
penpot-frontend:
image: "penpotapp/frontend:${PENPOT_VERSION:-latest}"
restart: always
ports:
- 9001:8080
# ports:
# - 9001:8080
volumes:
- penpot_assets:/opt/data/assets
@@ -102,33 +102,22 @@ services:
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.services.penpot.loadbalancer.server.port=8080"
# Prod Router
- "traefik.http.routers.penpot.rule=Host(`penpot.forust.xyz`)"
- "traefik.http.routers.penpot.entrypoints=websecure"
- "traefik.http.routers.penpot.middlewares=security-headers@file"
- "traefik.http.routers.penpot.service=penpot"
- "traefik.http.routers.penpot.tls=true"
- "traefik.http.services.penpot.loadbalancer.server.port=8080"
# Local Router
- "traefik.http.routers.penpot-local.rule=Host(`penpot.workstation.internal`) || Host(`penpot.internal`)"
- "traefik.http.routers.penpot-local.rule=Host(`penpot.workstation.internal`)"
- "traefik.http.routers.penpot-local.entrypoints=websecure"
- "traefik.http.routers.penpot-local.middlewares=security-headers@file"
- "traefik.http.routers.penpot-local.service=penpot"
- "traefik.http.routers.penpot-local.tls=true"
# Dev Router
- "traefik.http.routers.penpot-dev.rule=Host(`penpot.gigaforust.internal`)"
- "traefik.http.routers.penpot-dev.entrypoints=websecure"
- "traefik.http.routers.penpot-dev.middlewares=security-headers@file"
- "traefik.http.routers.penpot-dev.service=penpot"
- "traefik.http.routers.penpot-dev.tls=true"
environment:
<<: [ *penpot-flags, *penpot-http-body-size ]
penpot-backend:
image: "penpotapp/backend:${PENPOT_VERSION:-latest}"
restart: always
+9 -24
View File
@@ -1,54 +1,39 @@
services:
portainer:
container_name: portainer
image: portainer/portainer-ce:latest
container_name: portainer
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./portainer_data:/data
- data:/data
ports:
- 9443:9443
- 9000:9000
# - 8000:8000 # Remove if you do not intend to use Edge Agents
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
# Prod Router
- "traefik.http.routers.portainer.rule=Host(`portainer.forust.xyz`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.routers.portainer.middlewares=security-headers@file"
- "traefik.http.routers.portainer.service=portainer"
- "traefik.http.routers.portainer.tls=true"
- "traefik.http.services.portainer.loadbalancer.server.port=9443"
- "traefik.http.services.portainer.loadbalancer.server.scheme=https"
- "traefik.http.services.portainer.loadbalancer.serverstransport=insecureTransport@file"
# Local Router
- "traefik.http.routers.portainer-local.rule=Host(`portainer.workstation.internal`) || Host(`portainer.internal`)"
- "traefik.http.routers.portainer-local.rule=Host(`portainer.workstation.internal`)"
- "traefik.http.routers.portainer-local.entrypoints=websecure"
- "traefik.http.routers.portainer-local.middlewares=security-headers@file"
- "traefik.http.routers.portainer-local.service=portainer"
- "traefik.http.routers.portainer-local.tls=true"
# Dev Router
- "traefik.http.routers.portainer-dev.rule=Host(`portainer.gigaforust.internal`)"
- "traefik.http.routers.portainer-dev.entrypoints=websecure"
- "traefik.http.routers.portainer-dev.middlewares=security-headers@file"
- "traefik.http.routers.portainer-dev.service=portainer"
- "traefik.http.routers.portainer-dev.tls=true"
# Glance Metadata
- glance.name=Portainer
- glance.url=https://portainer.forust.xyz/
- glance.description=Portainer is a lightweight management UI which allows you to easily manage your Docker environments.
networks:
- proxy
volumes:
portainer_data:
name: portainer_data
data:
networks:
default:
name: portainer_network
proxy:
external: true
+6 -18
View File
@@ -3,44 +3,32 @@ services:
image: ghcr.io/lukegus/termix:latest
container_name: termix
restart: unless-stopped
ports:
- "3331:8080"
# ports:
# - "3331:8080"
volumes:
- ./termix-data:/app/data
- data:/app/data
environment:
PORT: "8080"
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.services.termix.loadbalancer.server.port=8080"
# Prod Router
- "traefik.http.routers.termix.rule=Host(`termix.forust.xyz`)"
- "traefik.http.routers.termix.entrypoints=websecure"
- "traefik.http.routers.termix.middlewares=security-headers@file"
- "traefik.http.routers.termix.service=termix"
- "traefik.http.routers.termix.tls=true"
- "traefik.http.services.termix.loadbalancer.server.port=8080"
# Local Router
- "traefik.http.routers.termix-local.rule=Host(`termix.workstation.internal`) || Host(`termix.internal`)"
- "traefik.http.routers.termix-local.rule=Host(`termix.workstation.internal`)"
- "traefik.http.routers.termix-local.entrypoints=websecure"
- "traefik.http.routers.termix-local.middlewares=security-headers@file"
- "traefik.http.routers.termix-local.service=termix"
- "traefik.http.routers.termix-local.tls=true"
# Dev Router
- "traefik.http.routers.termix-dev.rule=Host(`termix.gigaforust.internal`)"
- "traefik.http.routers.termix-dev.entrypoints=websecure"
- "traefik.http.routers.termix-dev.middlewares=security-headers@file"
- "traefik.http.routers.termix-dev.service=termix"
- "traefik.http.routers.termix-dev.tls=true"
networks:
- proxy
networks:
proxy:
external: true
volumes:
termix-data:
driver: local
data:
+15 -22
View File
@@ -17,13 +17,17 @@ services:
# EntryPoints
- "--entryPoints.web.address=:80"
- "--entryPoints.web.http.redirections.entryPoint.scheme=https"
- "--entryPoints.web.http.redirections.entryPoint.to=websecure"
- "--entryPoints.websecure.address=:443"
- "--entryPoints.websecure.http.middlewares=error-pages@docker"
- "--entryPoints.websecure.http.tls=true"
- "--entryPoints.ssh.address=:2221"
# Let's Encrypt
- "--certificatesresolvers.letsencrypt.acme.email=${EMAIL}"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
# # STAGING
# - "--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
@@ -31,15 +35,14 @@ services:
# Cloudflare
- "--entryPoints.web.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"
- "--entryPoints.websecure.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"
# # Logging
# - "--log.level=INFO"
# - "--log.filePath=/var/log/traefik/traefik.log"
# - "--accesslog=true"
# - "--accesslog.filepath=/var/log/traefik/access.log"
# Logging
- "--log.level=INFO"
- "--log.filePath=/var/log/traefik/traefik.log"
- "--log.format=json"
- "--accesslog=true"
- "--accesslog.filepath=/var/log/traefik/access.log"
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# Prod Router (Dash)
- "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.forust.xyz`)"
@@ -47,42 +50,32 @@ services:
- "traefik.http.routers.traefik-dashboard.middlewares=security-chain@file"
- "traefik.http.routers.traefik-dashboard.service=api@internal"
- "traefik.http.routers.traefik-dashboard.tls=true"
# Local Router
- "traefik.http.routers.traefik-dashboard-local.rule=Host(`traefik.workstation.internal`) || Host(`traefik.internal`)"
- "traefik.http.routers.traefik-dashboard-local.rule=Host(`traefik.workstation.internal`)"
- "traefik.http.routers.traefik-dashboard-local.entrypoints=websecure"
- "traefik.http.routers.traefik-dashboard-local.middlewares=security-headers@file"
- "traefik.http.routers.traefik-dashboard-local.service=api@internal"
- "traefik.http.routers.traefik-dashboard-local.tls=true"
# Dev Router
- "traefik.http.routers.traefik-dashboard-dev.rule=Host(`traefik.gigaforust.internal`)"
- "traefik.http.routers.traefik-dashboard-dev.entrypoints=websecure"
- "traefik.http.routers.traefik-dashboard-dev.middlewares=security-chain@file"
- "traefik.http.routers.traefik-dashboard-dev.service=api@internal"
- "traefik.http.routers.traefik-dashboard-dev.tls=true"
# Glance Metadata
- glance.name=Traefik
- glance.url=https://traefik.forust.xyz/
- glance.description=Traefik is a modern reverse proxy and load balancer
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./dynamic:/etc/traefik/dynamic:ro
- ./certs:/certs:ro
- ./logs:/var/log/traefik
- ./letsencrypt:/letsencrypt
ports:
- "80:80"
- "443:443"
networks:
- proxy
environment:
- TZ=Europe/Bratislava
networks:
proxy:
external: true
+23
View File
@@ -0,0 +1,23 @@
http:
routers:
fs1-public:
rule: "Host(`fs1.domain.xyz`)"
entrypoints:
- websecure
service: fs1
middlewares:
- security-chain@file
tls: {}
fs1-workstation:
rule: "Host(`fs1.workstation.internal`)"
entrypoints:
- websecure
service: fs1
tls: {}
services:
fs1:
loadBalancer:
servers:
- url: "http://127.0.0.1:3923" # Copyparty port example
+5
View File
@@ -1,5 +1,10 @@
http:
middlewares:
# HTTPS Redirect
redirect-https:
redirectScheme:
scheme: https
permanent: true
# Cloudflare IP Whitelist
cloudflare-ipwhitelist:
ipWhiteList:
-2
View File
@@ -15,7 +15,6 @@ http:
rule: "Host(`nextcloud.workstation.internal`)"
entrypoints:
- websecure
- web
service: nextcloud
middlewares:
- nextcloud-chain
@@ -26,7 +25,6 @@ http:
rule: "Host(`nextcloud.gigaforust.internal`)"
entrypoints:
- websecure
- web
service: nextcloud
middlewares:
- nextcloud-chain
-15
View File
@@ -1,10 +1,4 @@
http:
middlewares:
redirect-https:
redirectScheme:
scheme: https
permanent: true
routers:
acme-challenge-exempt:
rule: "PathPrefix(`/.well-known/acme-challenge`)"
@@ -12,12 +6,3 @@ http:
- web
service: noop@internal
priority: 100
http-catchall:
rule: "HostRegexp(`{host:.+}`)"
entryPoints:
- web
middlewares:
- redirect-https
service: noop@internal
priority: 1
+4 -6
View File
@@ -2,16 +2,14 @@
tls:
certificates:
# Cloudflare Origin CA *.forust.xyz
- certFile: /certs/cloudflare.pem
keyFile: /certs/cloudflare.key
- certFile: /certs/xdfnx.pem
keyFile: /certs/xdfnx.key
# - certFile: /certs/cloudflare.pem
# keyFile: /certs/cloudflare.key
stores:
default:
defaultCertificate:
# Fallback local certificate
certFile: /certs/gigaforust.internal+1.pem
keyFile: /certs/gigaforust.internal+1-key.pem
certFile: /certs/local.pem
keyFile: /certs/local-key.pem
options:
default:
+8 -10
View File
@@ -1,34 +1,32 @@
services:
uptime-kuma:
image: louislam/uptime-kuma:2
restart: unless-stopped
container_name: uptime-kuma
restart: unless-stopped
volumes:
- ./data:/app/data
ports:
# <Host Port>:<Container Port>
- "3001:3001"
- data:/app/data
# ports:
# - "3001:3001"
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001"
# Prod Router
- "traefik.http.routers.uptime-kuma.rule=Host(`uptime.forust.xyz`)"
- "traefik.http.routers.uptime-kuma.entrypoints=websecure"
- "traefik.http.routers.uptime-kuma.tls=true"
- "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001"
# Local Router
- "traefik.http.routers.uptime-kuma-local.rule=Host(`uptime.workstation.internal`) || Host(`uptime.internal`)"
- "traefik.http.routers.uptime-kuma-local.rule=Host(`uptime.workstation.internal`)"
- "traefik.http.routers.uptime-kuma-local.entrypoints=websecure"
- "traefik.http.routers.uptime-kuma-local.tls=true"
# Dev Router
- "traefik.http.routers.uptime-kuma-dev.rule=Host(`uptime.gigaforust.internal`)"
- "traefik.http.routers.uptime-kuma-dev.entrypoints=websecure"
- "traefik.http.routers.uptime-kuma-dev.tls=true"
networks:
- proxy
volumes:
data:
networks:
proxy:
external: true