diff --git a/_tmp_Caddyfile.txt b/_tmp_Caddyfile.txt
new file mode 100644
index 0000000..12e1a2f
--- /dev/null
+++ b/_tmp_Caddyfile.txt
@@ -0,0 +1,70 @@
+# Global options
+{
+ auto_https disable_redirects
+ email k.radikal@gmail.com
+}
+
+# Consolidated wildcard site block
+*.klhoud.com, http://*.klhoud.com {
+ # Automatic HTTPS via DNS challenge for all subdomains
+ tls {
+ dns cloudflare {$CLOUDFLARE_API_TOKEN}
+ }
+
+ # Proxmox
+ @proxmox host proxmox.klhoud.com
+ handle @proxmox {
+ reverse_proxy https://192.168.0.112:8006 {
+ # Required for connecting to Proxmox's self-signed/untrusted certificate
+ transport http {
+ tls_insecure_skip_verify
+ versions 1.1
+ }
+ }
+ }
+
+ # AdGuard Home
+ @adguard host adguard.klhoud.com
+ handle @adguard {
+ reverse_proxy 192.168.0.170:80
+ }
+
+ # Dockge
+ @dockge host dockge.klhoud.com
+ handle @dockge {
+ reverse_proxy 192.168.0.12:5001
+ }
+
+ # Nextcloud
+ @cloud host cloud.klhoud.com
+ handle @cloud {
+ reverse_proxy 192.168.0.12:8080
+ # Nextcloud-specific headers
+ header Strict-Transport-Security "max-age=31536000;"
+
+ # Handle large file uploads
+ request_body {
+ max_size 10GB
+ }
+ }
+
+ # Coolify (control plane runs on LXC 103). Upstream HTTPS to 443 because
+ # coolify-proxy's HTTP entry forces a redirect-to-https which would loop
+ # back through us. HTTPS upstream hits the no-redirect routes; cert is
+ # self-signed since coolify-proxy's ACME for coolify.klhoud.com fails
+ # (DNS points at us, not it), so we skip verify.
+ # coolify-proxy (Traefik) on LXC 103 listens on :80. We added a custom
+ # higher-priority router in /data/coolify/proxy/dynamic/coolify-noredirect.yaml
+ # there to bypass Coolify's auto-generated http->https redirect (which would
+ # loop us). WebSocket routes (/app, /terminal/ws) work natively on :80.
+ @coolify host coolify.klhoud.com
+ handle @coolify {
+ reverse_proxy 192.168.0.47:80
+ }
+
+ # IMPORTANT: Fallback Handler
+ # This prevents non-matching requests from falling through and failing.
+ handle {
+ respond "Not Found" 404
+ }
+}
diff --git a/_tmp_coolify-noredirect.yaml b/_tmp_coolify-noredirect.yaml
new file mode 100644
index 0000000..ecc3f86
--- /dev/null
+++ b/_tmp_coolify-noredirect.yaml
@@ -0,0 +1,20 @@
+# Override file added by us (homelab-configs).
+# Coolify auto-generates /data/coolify/proxy/dynamic/coolify.yaml with a
+# redirect-to-https middleware on the HTTP router. That redirect causes a
+# loop with our upstream alpine-caddy (LXC 101) which already terminates TLS.
+# This higher-priority router wins for the UI but explicitly excludes the
+# WebSocket paths so they keep using Coolify's own routers (coolify-realtime-ws
+# for /app, coolify-terminal-ws for /terminal/ws).
+http:
+ routers:
+ coolify-http-noredirect:
+ entryPoints:
+ - http
+ service: coolify-noredirect-svc
+ rule: 'Host(`coolify.klhoud.com`) && !PathPrefix(`/app`) && !PathPrefix(`/terminal/ws`)'
+ priority: 1000
+ services:
+ coolify-noredirect-svc:
+ loadBalancer:
+ servers:
+ - url: 'http://coolify:8080'
diff --git a/pve/_meta/host-overview.txt b/pve/_meta/host-overview.txt
new file mode 100644
index 0000000..ae64586
--- /dev/null
+++ b/pve/_meta/host-overview.txt
@@ -0,0 +1,72 @@
+=== PVE host info ===
+pve
+Linux pve 6.17.2-1-pve #1 SMP PREEMPT_DYNAMIC PMX 6.17.2-1 (2025-10-21T11:55Z) x86_64 GNU/Linux
+pve-manager/9.1.1/42db4a6cf33dac83 (running kernel: 6.17.2-1-pve)
+
+=== LXC list ===
+VMID Status Lock Name
+100 running alpine-adguard
+101 running alpine-caddy
+102 running dockge
+103 running coolify
+
+=== VM list ===
+
+=== Per-LXC config ===
+===LXC-100===
+arch: amd64
+cores: 1
+description:
%0A
+features: keyctl=1,nesting=1
+hostname: alpine-adguard
+memory: 256
+net0: name=eth0,bridge=vmbr0,hwaddr=BC:24:11:A4:76:5F,ip=dhcp,type=veth
+onboot: 1
+ostype: alpine
+rootfs: local-lvm:vm-100-disk-0,size=1G
+swap: 512
+tags: adblock;alpine;community-script
+unprivileged: 1
+
+===LXC-101===
+arch: amd64
+cores: 1
+description: %0A
+features: keyctl=1,nesting=1
+hostname: alpine-caddy
+memory: 256
+net0: name=eth0,bridge=vmbr0,hwaddr=BC:24:11:4E:3F:97,ip=dhcp,type=veth
+onboot: 1
+ostype: alpine
+rootfs: local-lvm:vm-101-disk-0,size=3G
+swap: 512
+tags: community-script;webserver
+unprivileged: 1
+
+===LXC-102===
+arch: amd64
+cores: 2
+description: %0A
+features: keyctl=1,nesting=1
+hostname: dockge
+memory: 2048
+net0: name=eth0,bridge=vmbr0,hwaddr=BC:24:11:FA:D7:D4,ip=dhcp,type=veth
+onboot: 1
+ostype: debian
+rootfs: local-lvm:vm-102-disk-0,size=18G
+swap: 512
+tags: community-script;docker
+unprivileged: 1
+
+===LXC-103===
+arch: amd64
+cores: 2
+features: nesting=1
+hostname: coolify
+memory: 4096
+net0: name=eth0,bridge=vmbr0,firewall=1,hwaddr=BC:24:11:36:EC:64,ip=dhcp,type=veth
+ostype: debian
+rootfs: local-lvm:vm-103-disk-0,size=60G
+swap: 512
+unprivileged: 1
+
diff --git a/pve/lxc-101-alpine-caddy/_dir-listing.txt b/pve/lxc-101-alpine-caddy/_dir-listing.txt
new file mode 100644
index 0000000..70b3403
--- /dev/null
+++ b/pve/lxc-101-alpine-caddy/_dir-listing.txt
@@ -0,0 +1,10 @@
+total 28
+drwxr-xr-x 2 root root 4096 May 6 09:27 .
+drwxr-xr-x 32 root root 4096 Apr 30 02:29 ..
+-rw-r--r-- 1 root root 1471 May 6 09:27 Caddyfile
+-rw-r--r-- 1 root root 1390 Nov 25 05:04 Caddyfile.bak.1777516902
+-rw-r--r-- 1 root root 1444 Apr 30 02:43 Caddyfile.bak.1777517752
+-rw-r--r-- 1 root root 1473 May 6 09:27 Caddyfile.bak.1778059646
+-rw------- 1 root root 62 Oct 13 2025 caddy.env
+---
+ls: /etc/caddy/Caddyfile.bak.*: No such file or directory
diff --git a/pve/lxc-101-alpine-caddy/etc/caddy/Caddyfile b/pve/lxc-101-alpine-caddy/etc/caddy/Caddyfile
new file mode 100644
index 0000000..a8de7bd
--- /dev/null
+++ b/pve/lxc-101-alpine-caddy/etc/caddy/Caddyfile
@@ -0,0 +1,62 @@
+# Global options
+{
+ auto_https disable_redirects
+ email k.radikal@gmail.com
+}
+
+# Consolidated wildcard site block
+*.klhoud.com, http://*.klhoud.com {
+ # Automatic HTTPS via DNS challenge for all subdomains
+ tls {
+ dns cloudflare {$CLOUDFLARE_API_TOKEN}
+ }
+
+ # Proxmox
+ @proxmox host proxmox.klhoud.com
+ handle @proxmox {
+ reverse_proxy https://192.168.0.112:8006 {
+ # Required for connecting to Proxmox's self-signed/untrusted certificate
+ transport http {
+ tls_insecure_skip_verify
+ versions 1.1
+ }
+ }
+ }
+
+ # AdGuard Home
+ @adguard host adguard.klhoud.com
+ handle @adguard {
+ reverse_proxy 192.168.0.170:80
+ }
+
+ # Dockge
+ @dockge host dockge.klhoud.com
+ handle @dockge {
+ reverse_proxy 192.168.0.12:5001
+ }
+
+ # Nextcloud
+ @cloud host cloud.klhoud.com
+ handle @cloud {
+ reverse_proxy 192.168.0.12:8080
+ # Nextcloud-specific headers
+ header Strict-Transport-Security "max-age=31536000;"
+
+ # Handle large file uploads
+ request_body {
+ max_size 10GB
+ }
+ }
+
+ # Coolify
+ @coolify host coolify.klhoud.com
+ handle @coolify {
+ reverse_proxy 192.168.0.47:80
+ }
+
+ # IMPORTANT: Fallback Handler
+ # This prevents non-matching requests from falling through and failing.
+ handle {
+ respond "Not Found" 404
+ }
+}
diff --git a/pve/lxc-103-coolify/_docker-ps.txt b/pve/lxc-103-coolify/_docker-ps.txt
new file mode 100644
index 0000000..645e597
--- /dev/null
+++ b/pve/lxc-103-coolify/_docker-ps.txt
@@ -0,0 +1,6 @@
+coolify ghcr.io/coollabsio/coolify:4.0.0 Up 6 days (healthy)
+coolify-db postgres:15-alpine Up 6 days (healthy)
+coolify-redis redis:7-alpine Up 6 days (healthy)
+coolify-realtime ghcr.io/coollabsio/coolify-realtime:1.0.13 Up 6 days (healthy)
+coolify-sentinel ghcr.io/coollabsio/sentinel:0.0.21 Up 2 weeks (healthy)
+coolify-proxy traefik:v3.6 Up 5 months (healthy)
diff --git a/pve/lxc-103-coolify/data/coolify/proxy/docker-compose.yml b/pve/lxc-103-coolify/data/coolify/proxy/docker-compose.yml
new file mode 100644
index 0000000..332de4e
--- /dev/null
+++ b/pve/lxc-103-coolify/data/coolify/proxy/docker-compose.yml
@@ -0,0 +1,54 @@
+name: coolify-proxy
+networks:
+ coolify:
+ external: true
+services:
+ traefik:
+ container_name: coolify-proxy
+ image: 'traefik:v3.6'
+ restart: unless-stopped
+ extra_hosts:
+ - 'host.docker.internal:host-gateway'
+ networks:
+ - coolify
+ ports:
+ - '80:80'
+ - '443:443'
+ - '443:443/udp'
+ - '8080:8080'
+ healthcheck:
+ test: 'wget -qO- http://localhost:80/ping || exit 1'
+ interval: 4s
+ timeout: 2s
+ retries: 5
+ volumes:
+ - '/var/run/docker.sock:/var/run/docker.sock:ro'
+ - '/data/coolify/proxy/:/traefik'
+ command:
+ - '--ping=true'
+ - '--ping.entrypoint=http'
+ - '--api.dashboard=true'
+ - '--entrypoints.http.address=:80'
+ - '--entrypoints.http.forwardedheaders.trustedips=192.168.0.40'
+ - '--entrypoints.https.address=:443'
+ - '--entrypoints.https.forwardedheaders.trustedips=192.168.0.40'
+ - '--entrypoints.http.http.encodequerysemicolons=true'
+ - '--entryPoints.http.http2.maxConcurrentStreams=250'
+ - '--entrypoints.https.http.encodequerysemicolons=true'
+ - '--entryPoints.https.http2.maxConcurrentStreams=250'
+ - '--entrypoints.https.http3'
+ - '--providers.file.directory=/traefik/dynamic/'
+ - '--providers.file.watch=true'
+ - '--certificatesresolvers.letsencrypt.acme.httpchallenge=true'
+ - '--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http'
+ - '--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json'
+ - '--api.insecure=false'
+ - '--providers.docker=true'
+ - '--providers.docker.exposedbydefault=false'
+ labels:
+ - traefik.enable=true
+ - traefik.http.routers.traefik.entrypoints=http
+ - traefik.http.routers.traefik.service=api@internal
+ - traefik.http.services.traefik.loadbalancer.server.port=8080
+ - coolify.managed=true
+ - coolify.proxy=true
\ No newline at end of file
diff --git a/pve/lxc-103-coolify/data/coolify/proxy/dynamic/Caddyfile b/pve/lxc-103-coolify/data/coolify/proxy/dynamic/Caddyfile
new file mode 100644
index 0000000..1a8d379
--- /dev/null
+++ b/pve/lxc-103-coolify/data/coolify/proxy/dynamic/Caddyfile
@@ -0,0 +1 @@
+import /dynamic/*.caddy
diff --git a/pve/lxc-103-coolify/data/coolify/proxy/dynamic/coolify.yaml b/pve/lxc-103-coolify/data/coolify/proxy/dynamic/coolify.yaml
new file mode 100644
index 0000000..a9c1c78
--- /dev/null
+++ b/pve/lxc-103-coolify/data/coolify/proxy/dynamic/coolify.yaml
@@ -0,0 +1,65 @@
+# This file is automatically generated by Coolify.
+# Do not edit it manually (only if you know what are you doing).
+
+http:
+ middlewares:
+ redirect-to-https:
+ redirectscheme:
+ scheme: https
+ gzip:
+ compress: true
+ routers:
+ coolify-http:
+ middlewares:
+ - redirect-to-https
+ entryPoints:
+ - http
+ service: coolify
+ rule: Host(`coolify.klhoud.com`)
+ coolify-realtime-ws:
+ entryPoints:
+ - http
+ service: coolify-realtime
+ rule: 'Host(`coolify.klhoud.com`) && PathPrefix(`/app`)'
+ coolify-terminal-ws:
+ entryPoints:
+ - http
+ service: coolify-terminal
+ rule: 'Host(`coolify.klhoud.com`) && PathPrefix(`/terminal/ws`)'
+ coolify-https:
+ entryPoints:
+ - https
+ service: coolify
+ rule: Host(`coolify.klhoud.com`)
+ tls:
+ certresolver: letsencrypt
+ coolify-realtime-wss:
+ entryPoints:
+ - https
+ service: coolify-realtime
+ rule: 'Host(`coolify.klhoud.com`) && PathPrefix(`/app`)'
+ tls:
+ certresolver: letsencrypt
+ coolify-terminal-wss:
+ entryPoints:
+ - https
+ service: coolify-terminal
+ rule: 'Host(`coolify.klhoud.com`) && PathPrefix(`/terminal/ws`)'
+ tls:
+ certresolver: letsencrypt
+ services:
+ coolify:
+ loadBalancer:
+ servers:
+ -
+ url: 'http://coolify:8080'
+ coolify-realtime:
+ loadBalancer:
+ servers:
+ -
+ url: 'http://coolify-realtime:6001'
+ coolify-terminal:
+ loadBalancer:
+ servers:
+ -
+ url: 'http://coolify-realtime:6002'
diff --git a/pve/lxc-103-coolify/data/coolify/proxy/dynamic/default_redirect_503.yaml b/pve/lxc-103-coolify/data/coolify/proxy/dynamic/default_redirect_503.yaml
new file mode 100644
index 0000000..bfd0202
--- /dev/null
+++ b/pve/lxc-103-coolify/data/coolify/proxy/dynamic/default_redirect_503.yaml
@@ -0,0 +1,18 @@
+# This file is generated by Coolify, do not edit it manually.
+# Disable the default redirect to customize (only if you know what are you doing).
+
+http:
+ routers:
+ catchall:
+ entryPoints:
+ - http
+ - https
+ service: noop
+ rule: PathPrefix(`/`)
+ tls:
+ certResolver: letsencrypt
+ priority: -1000
+ services:
+ noop:
+ loadBalancer:
+ servers: { }
diff --git a/pve/lxc-103-coolify/data/coolify/source/.env b/pve/lxc-103-coolify/data/coolify/source/.env
new file mode 100644
index 0000000..aef9a1d
--- /dev/null
+++ b/pve/lxc-103-coolify/data/coolify/source/.env
@@ -0,0 +1,17 @@
+APP_ID=4ade2d03d47e25dbd2477aebeea02f68
+APP_NAME=Coolify
+APP_KEY=base64:/NXqvQqLEoiL2l95unMEgIrmNISZWCN3gkn8C+70ArU=
+APP_URL=https://coolify.klhoud.com
+
+DB_USERNAME=coolify
+DB_PASSWORD=NK7UZfGH6s+xK1FCWhZ16UIW5+XLSyJWUW/SAeiUqr0=
+REDIS_PASSWORD=hFFgieL5zun4R259weXB5mwB+obiNqzgFbZ+/TybQwM=
+PUSHER_APP_ID=a62f9819f9aa86b34b07973a11f6f844029c1a5a1cfc96e36ee93629ba0194f6
+PUSHER_APP_KEY=904493523fc1e3b042fdac7ee119aa44220799fbbb0199259d911a1860ea3c19
+PUSHER_APP_SECRET=5ec83d045498cfaeba51bc0bd6a84957a81892383c98a1a2f24e4a6f192f6387
+ROOT_USERNAME=
+ROOT_USER_EMAIL=
+ROOT_USER_PASSWORD=
+REGISTRY_URL=ghcr.io
+DOCKER_ADDRESS_POOL_BASE=10.0.0.0/8
+DOCKER_ADDRESS_POOL_SIZE=24
diff --git a/pve/lxc-103-coolify/data/coolify/source/docker-compose.prod.yml b/pve/lxc-103-coolify/data/coolify/source/docker-compose.prod.yml
new file mode 100644
index 0000000..901aeb8
--- /dev/null
+++ b/pve/lxc-103-coolify/data/coolify/source/docker-compose.prod.yml
@@ -0,0 +1,90 @@
+services:
+ coolify:
+ image: "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE:-latest}"
+ volumes:
+ - type: bind
+ source: /data/coolify/source/.env
+ target: /var/www/html/.env
+ read_only: true
+ - /data/coolify/ssh:/var/www/html/storage/app/ssh
+ - /data/coolify/applications:/var/www/html/storage/app/applications
+ - /data/coolify/databases:/var/www/html/storage/app/databases
+ - /data/coolify/services:/var/www/html/storage/app/services
+ - /data/coolify/backups:/var/www/html/storage/app/backups
+ environment:
+ - APP_ENV=${APP_ENV:-production}
+ - PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT:-256M}
+ - PHP_FPM_PM_CONTROL=${PHP_FPM_PM_CONTROL:-dynamic}
+ - PHP_FPM_PM_START_SERVERS=${PHP_FPM_PM_START_SERVERS:-1}
+ - PHP_FPM_PM_MIN_SPARE_SERVERS=${PHP_FPM_PM_MIN_SPARE_SERVERS:-1}
+ - PHP_FPM_PM_MAX_SPARE_SERVERS=${PHP_FPM_PM_MAX_SPARE_SERVERS:-10}
+ env_file:
+ - /data/coolify/source/.env
+ ports:
+ - "${APP_PORT:-8000}:8080"
+ expose:
+ - "${APP_PORT:-8000}"
+ healthcheck:
+ test: curl --fail http://127.0.0.1:8080/api/health || exit 1
+ interval: 5s
+ retries: 10
+ timeout: 2s
+ depends_on:
+ postgres:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ soketi:
+ condition: service_healthy
+ postgres:
+ volumes:
+ - coolify-db:/var/lib/postgresql/data
+ environment:
+ POSTGRES_USER: "${DB_USERNAME}"
+ POSTGRES_PASSWORD: "${DB_PASSWORD}"
+ POSTGRES_DB: "${DB_DATABASE:-coolify}"
+ healthcheck:
+ test: [ "CMD-SHELL", "pg_isready -U ${DB_USERNAME}", "-d", "${DB_DATABASE:-coolify}" ]
+ interval: 5s
+ retries: 10
+ timeout: 2s
+ redis:
+ command: redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD}
+ environment:
+ REDIS_PASSWORD: "${REDIS_PASSWORD}"
+ volumes:
+ - coolify-redis:/data
+ healthcheck:
+ test: redis-cli ping
+ interval: 5s
+ retries: 10
+ timeout: 2s
+ soketi:
+ image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.13'
+ ports:
+ - "${SOKETI_PORT:-6001}:6001"
+ - "6002:6002"
+ volumes:
+ - /data/coolify/ssh:/var/www/html/storage/app/ssh
+ environment:
+ APP_NAME: "${APP_NAME:-Coolify}"
+ SOKETI_DEBUG: "${SOKETI_DEBUG:-false}"
+ SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID}"
+ SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY}"
+ SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET}"
+ SOKETI_HOST: "${SOKETI_HOST:-0.0.0.0}"
+ healthcheck:
+ test: [ "CMD-SHELL", "wget -qO- http://127.0.0.1:6001/ready && wget -qO- http://127.0.0.1:6002/ready || exit 1" ]
+ interval: 5s
+ retries: 10
+ timeout: 2s
+
+volumes:
+ coolify-db:
+ name: coolify-db
+ coolify-redis:
+ name: coolify-redis
+
+networks:
+ coolify:
+ external: true
diff --git a/pve/lxc-103-coolify/data/coolify/source/docker-compose.yml b/pve/lxc-103-coolify/data/coolify/source/docker-compose.yml
new file mode 100644
index 0000000..0fd3dda
--- /dev/null
+++ b/pve/lxc-103-coolify/data/coolify/source/docker-compose.yml
@@ -0,0 +1,37 @@
+services:
+ coolify:
+ container_name: coolify
+ restart: always
+ working_dir: /var/www/html
+ extra_hosts:
+ - host.docker.internal:host-gateway
+ networks:
+ - coolify
+ depends_on:
+ - postgres
+ - redis
+ - soketi
+ postgres:
+ image: postgres:15-alpine
+ container_name: coolify-db
+ restart: always
+ networks:
+ - coolify
+ redis:
+ image: redis:7-alpine
+ container_name: coolify-redis
+ restart: always
+ networks:
+ - coolify
+ soketi:
+ container_name: coolify-realtime
+ extra_hosts:
+ - host.docker.internal:host-gateway
+ restart: always
+ networks:
+ - coolify
+networks:
+ coolify:
+ name: coolify
+ driver: bridge
+ external: false