- new compose templates + READMEs for the three LXC apps (110-112) - Makefile deploy targets (rackula pre-owns its data dir for uid 1001) - Caddy vhosts for all four, incl. Hermes /auth/login redirect workaround - firewall docs: CADDY->TRUSTED rules for LXCs 110-113
177 lines
6.6 KiB
Makefile
177 lines
6.6 KiB
Makefile
# Homelab Blueprint Makefile
|
|
|
|
.PHONY: help check-env status deploy-caddy deploy-ha deploy-arr deploy-immich deploy-dns deploy-postgres deploy-gramps deploy-beszel deploy-beszel-hub deploy-proxy deploy-youtarr deploy-bento-pdf deploy-it-tools deploy-paperless deploy-vaultwarden deploy-rackula deploy-koffan deploy-snapotter
|
|
|
|
# Default shell
|
|
SHELL := /bin/bash
|
|
|
|
# Configuration
|
|
ENV_FILE ?= .env
|
|
STACKS_DIR := docker-compose-files
|
|
SUBSTITUTE_SCRIPT := ./substitute_env.sh
|
|
|
|
help:
|
|
@echo "Homelab Blueprint Management"
|
|
@echo ""
|
|
@echo "Usage: make <target>"
|
|
@echo ""
|
|
@echo "Deploy Targets:"
|
|
@echo " deploy-caddy - Deploy Caddy Reverse Proxy"
|
|
@echo " deploy-ha - Deploy Home Assistant (uses setup script)"
|
|
@echo " deploy-arr - Deploy Arr Stack (Media Management)"
|
|
@echo " deploy-immich - Deploy Immich (Photos)"
|
|
@echo " deploy-dns - Deploy Technitium DNS"
|
|
@echo " deploy-postgres - Deploy PostgreSQL"
|
|
@echo " deploy-gramps - Deploy Gramps Web"
|
|
@echo " deploy-beszel - Deploy Beszel Agent"
|
|
@echo " deploy-beszel-hub - Deploy Beszel Hub (dashboard)"
|
|
@echo " deploy-proxy - Deploy Beszel Socket Proxy"
|
|
@echo " deploy-youtarr - Deploy Youtarr"
|
|
@echo " deploy-bento-pdf - Deploy BentoPDF"
|
|
@echo " deploy-it-tools - Deploy IT-Tools"
|
|
@echo " deploy-paperless - Deploy Paperless-ngx"
|
|
@echo " deploy-vaultwarden - Deploy Vaultwarden"
|
|
@echo " deploy-rackula - Deploy Rackula (rack designer)"
|
|
@echo " deploy-koffan - Deploy Koffan (grocery list)"
|
|
@echo " deploy-snapotter - Deploy SnapOtter (file toolkit)"
|
|
@echo ""
|
|
@echo "Utility Targets:"
|
|
@echo " status - Show status of all deployed stacks"
|
|
@echo " check-env - Validate .env file against templates"
|
|
@echo " ip-check - Check IP leak protection for VPN containers"
|
|
@echo " clean - Remove generated docker-compose.*.yml files"
|
|
|
|
check-env:
|
|
@target_dir=$$( [ -n "$(STACK)" ] && echo "$(STACKS_DIR)/$(STACK)" || echo "$(STACKS_DIR)/*" ); \
|
|
for dir in $$target_dir; do \
|
|
if [ -d "$$dir" ]; then \
|
|
stack=$$(basename $$dir); \
|
|
local_env="$$dir/.env"; \
|
|
placeholders=$$(find "$$dir" -type f \( -name '*template*' -o -name '*.yaml' -o -name '*.yml' \) -print0 | \
|
|
xargs -0 awk '!/^[[:space:]]*#/ { while (match($$0, /\{\{[A-Za-z_][A-Za-z0-9_]*\}\}/)) { print substr($$0, RSTART + 2, RLENGTH - 4); $$0 = substr($$0, RSTART + RLENGTH) } }' | \
|
|
sort -u || true); \
|
|
if [ -n "$$placeholders" ]; then \
|
|
echo "Validating environment for: $$stack"; \
|
|
ERR=0; \
|
|
for var in $$placeholders; do \
|
|
if ! grep -qE "^$${var}=" $(ENV_FILE) 2>/dev/null && ! grep -qE "^$${var}=" "$$local_env" 2>/dev/null; then \
|
|
echo " [MISSING] $$var"; \
|
|
ERR=1; \
|
|
fi; \
|
|
done; \
|
|
if [ $$ERR -eq 1 ]; then echo "Error: Missing variables for $$stack. Check your .env files."; exit 1; fi; \
|
|
fi; \
|
|
fi; \
|
|
done
|
|
|
|
status:
|
|
@echo "--- Stack Status ---"
|
|
@for f in docker-compose.*.yml; do \
|
|
if [ -f "$$f" ]; then \
|
|
echo "Stack: $$f"; \
|
|
docker compose -f "$$f" ps; \
|
|
echo ""; \
|
|
fi \
|
|
done
|
|
|
|
ip-check:
|
|
@test -x ./ipfconfig || { echo "Error: ipfconfig script not found or not executable." >&2; exit 1; }
|
|
@./ipfconfig
|
|
|
|
clean:
|
|
@echo "Removing generated compose files..."
|
|
@rm -f docker-compose.*.yml
|
|
|
|
# Individual Stack Deployments
|
|
#
|
|
# deploy_stack(stack-dir, compose-suffix) - shared recipe for the common case:
|
|
# check-env -> substitute template -> docker compose up -d
|
|
define deploy_stack
|
|
@$(MAKE) check-env STACK=$(1)
|
|
@echo "Deploying $(1)..."
|
|
@$(SUBSTITUTE_SCRIPT) $(STACKS_DIR)/$(1)/template.yaml docker-compose.$(2).yml $(ENV_FILE) $(STACKS_DIR)/$(1)/.env
|
|
@docker compose -f docker-compose.$(2).yml up -d
|
|
endef
|
|
|
|
# Caddy is special: also renders the Caddyfile and forces an image rebuild.
|
|
deploy-caddy:
|
|
@$(MAKE) check-env STACK=caddy
|
|
@echo "Deploying Caddy..."
|
|
@$(SUBSTITUTE_SCRIPT) $(STACKS_DIR)/caddy/Caddyfile_template $(STACKS_DIR)/caddy/Caddyfile $(ENV_FILE) $(STACKS_DIR)/caddy/.env
|
|
@$(SUBSTITUTE_SCRIPT) $(STACKS_DIR)/caddy/template.yaml docker-compose.caddy.yml $(ENV_FILE) $(STACKS_DIR)/caddy/.env
|
|
@chmod 644 $(STACKS_DIR)/caddy/Caddyfile
|
|
@docker compose -f docker-compose.caddy.yml up -d --build
|
|
|
|
# HA uses an external setup script instead of substitute_env.sh.
|
|
deploy-ha:
|
|
@test -x ./setup-homeassistant.sh || { echo "Error: setup-homeassistant.sh not found or not executable." >&2; exit 1; }
|
|
@$(MAKE) check-env STACK=homeassistant
|
|
@echo "Deploying Home Assistant..."
|
|
@./setup-homeassistant.sh
|
|
@docker compose -f docker-compose.homeassistant.yml up -d
|
|
|
|
deploy-arr:
|
|
$(call deploy_stack,arr-stack,arr-stack)
|
|
|
|
deploy-immich:
|
|
$(call deploy_stack,immich,immich)
|
|
|
|
deploy-dns:
|
|
$(call deploy_stack,technitium,technitium)
|
|
|
|
deploy-postgres:
|
|
$(call deploy_stack,postgres,postgres)
|
|
|
|
deploy-gramps:
|
|
@$(MAKE) check-env STACK=gramps-web
|
|
@echo "Deploying gramps-web..."
|
|
@mkdir -p gramps_config
|
|
@$(SUBSTITUTE_SCRIPT) $(STACKS_DIR)/gramps-web/config/config_template.cfg gramps_config/config.cfg $(ENV_FILE) $(STACKS_DIR)/gramps-web/.env
|
|
@$(SUBSTITUTE_SCRIPT) $(STACKS_DIR)/gramps-web/template.yaml docker-compose.gramps.yml $(ENV_FILE) $(STACKS_DIR)/gramps-web/.env
|
|
@docker compose -f docker-compose.gramps.yml up -d
|
|
|
|
deploy-beszel:
|
|
$(call deploy_stack,beszel-agent,beszel)
|
|
|
|
deploy-beszel-hub:
|
|
$(call deploy_stack,beszel-hub,beszel-hub)
|
|
|
|
deploy-bento-pdf:
|
|
$(call deploy_stack,bento-pdf,bento-pdf)
|
|
|
|
deploy-it-tools:
|
|
$(call deploy_stack,it-tools,it-tools)
|
|
|
|
deploy-paperless:
|
|
$(call deploy_stack,paperless-ngx,paperless)
|
|
|
|
deploy-vaultwarden:
|
|
$(call deploy_stack,vaultwarden,vaultwarden)
|
|
|
|
# Rackula's API container runs as uid 1001 and needs its data dir pre-owned.
|
|
deploy-rackula:
|
|
@mkdir -p data
|
|
@chown 1001:1001 data 2>/dev/null || sudo chown 1001:1001 data
|
|
$(call deploy_stack,rackula,rackula)
|
|
|
|
deploy-koffan:
|
|
$(call deploy_stack,koffan,koffan)
|
|
|
|
deploy-snapotter:
|
|
$(call deploy_stack,snapotter,snapotter)
|
|
|
|
deploy-proxy:
|
|
$(call deploy_stack,socket-proxy,proxy)
|
|
|
|
deploy-youtarr:
|
|
$(call deploy_stack,youtarr,youtarr)
|
|
|
|
reload-caddy:
|
|
@docker compose -f docker-compose.caddy.yml ps --status running --services 2>/dev/null | grep -q '^caddy$$' \
|
|
|| { echo "Error: Caddy container is not running. Run 'make deploy-caddy' first." >&2; exit 1; }
|
|
@$(MAKE) check-env STACK=caddy
|
|
@echo "Performing zero-downtime Caddy reload..."
|
|
@$(SUBSTITUTE_SCRIPT) $(STACKS_DIR)/caddy/Caddyfile_template $(STACKS_DIR)/caddy/Caddyfile $(ENV_FILE) $(STACKS_DIR)/caddy/.env
|
|
@chmod 644 $(STACKS_DIR)/caddy/Caddyfile
|
|
@docker compose -f docker-compose.caddy.yml exec caddy caddy validate --config /etc/caddy/Caddyfile
|
|
@docker compose -f docker-compose.caddy.yml exec caddy caddy reload --config /etc/caddy/Caddyfile
|