--- # Where Nextcloud backup failure alerts are mailed (see containers/cloud-backup.yml). backup_alert_email: bastian@debyl.io bookstack_path: "{{ podman_volumes }}/bookstack" cam2ip_path: "{{ podman_volumes }}/cam2ip" cloud_path: "{{ podman_volumes }}/cloud" cloud_skudak_path: "{{ podman_volumes }}/skudakcloud" debyltech_path: "{{ podman_volumes }}/debyltech" # drone_path: removed - Drone CI decommissioned factorio_path: "{{ podman_volumes }}/factorio" fulfillr_path: "{{ podman_volumes }}/fulfillr" fulfillr_cases_table: "debyltech-cases-prod" fulfillr_tickets_table: "debyltech-tickets-prod" # Turso ecommerce store (self-hosted checkout). # PROD store URL (non-secret); the RW token `fulfillr_prod_store_auth_token` is in the vault. fulfillr_prod_store_database_url: "libsql://debyltech-store-prod-debyltech.aws-us-east-1.turso.io" # Staging back-office (fulfillr-dev.debyltech.com, port 9055) -> staging Turso store. # Its RW token is `fulfillr_dev_store_auth_token` and EasyPost test key is # `fulfillr_dev_easypost_api_key`, both in the encrypted vault. fulfillr_dev_path: "{{ podman_volumes }}/fulfillr-dev" fulfillr_dev_server_name: fulfillr-dev.debyltech.com fulfillr_dev_store_database_url: "libsql://debyltech-store-staging-debyltech.aws-us-east-1.turso.io" gregtime_path: "{{ podman_volumes }}/gregtime" hass_path: "{{ podman_volumes }}/hass" # nginx_path: removed - nginx no longer used # nosql_path: removed - nosql/redis no longer used partsy_path: "{{ podman_volumes }}/partsy" partsy_skudak_path: "{{ podman_volumes }}/partsy-skudak" photos_path: "{{ podman_volumes }}/photos" # rsvp.debyl.io -- invite-only RSVP app (~/src/rsvp-debylio). Both listeners are # published on loopback only: Caddy proxies the public one, and the admin one # only for LAN clients. Caddy runs with network: host, so 127.0.0.1 reaches them. rsvp_path: "{{ podman_volumes }}/rsvp" rsvp_public_port: 9080 rsvp_admin_port: 9081 # Named rather than hardcoded so the ML service can be stood up beside a broken # one without editing tasks. On 2026-08-28 a worker thread wedged in # uninterruptible sleep (D state) in exit_mmap, which made the container # unremovable -- it survived SIGKILL and `podman rm -f` and kept its name and # network alias until the host was rebooted. The reboot cleared it, so this # stays on the canonical name; see MACHINE_LEARNING_MODEL_TTL in photos.yml for # the change that stops it recurring. immich_ml_container: immich-machine-learning uptime_kuma_path: "{{ podman_volumes }}/uptime-kuma" uptime_kuma_personal_path: "{{ podman_volumes }}/uptime-kuma-personal" zomboid_path: "{{ podman_volumes }}/zomboid" # Whether to deploy and run the Zomboid server at all. # # Was off for months because it was the heaviest tenant on this host: ~9 GB # resident and ~10 MB/s of continuous log writes, which on a 4-core box also # running Gitea, Immich, Graylog and two Nextclouds saturated the QLC SSD and # starved CI -- a firmware build stretched to 17 minutes and one release job # was killed outright by Gitea's zombie-task timeout. # # Back on now that log growth is bounded: the container logs to a rotating # k8s-file instead of the shared journal, and logrotate caps the server's own # server-console.txt and Logs/ (see containers/home/zomboid.yml). Memory is # unchanged -- idle draw is nowhere near MAX_RAM. # # Pass -e zomboid_enabled=false to keep it down through a CI-heavy stretch. # World data under {{ zomboid_path }} is left in place either way. zomboid_enabled: true # Server name, and also the on-disk identity: PZ derives Server/.ini, # Saves/Multiplayer// and db/.db from it. Renaming it therefore # starts a brand-new world and leaves the previous one intact for rollback. zomboid_server_name: debbzoid zomboid_public_name: Debbzoid # Sophie 42 ships map_distanciado as its map mod. Muldraugh must stay last -- # the base map is the fallback layer. zomboid_map: "map_distanciado;Muldraugh, KY" # Zomboid RCON port for remote administration zomboid_rcon_port: "27015" # Discord channel the server's own chat bridge posts into. Build 42 replaced # B41's DiscordChannel/DiscordChannelID pair with DiscordChatChannel, which # takes a channel *name*, not a snowflake ID. zomboid_discord_chat_channel: zomboidbot # JVM heap, written into ProjectZomboid64.json by the container entrypoint. zomboid_min_ram: 8g zomboid_max_ram: 24g # The modlist itself lives in vars/zomboid_sophie_mods.yml -- 283 mod IDs and # 256 workshop items vendored from the upstream Sophie 42 preset. # # The modlist is still the upstream preset. The world settings no longer are: # files/zomboid/sophie/SandboxVars.lua is a snapshot of the running debbzoid # world taken 2026-08-31, not the preset as shipped. See the seed tasks in # containers/home/zomboid.yml. zomboid_preset_version: "sophie-42 mods @ 2026-07-31 / live world settings @ 2026-08-31" # Mods subtracted from the vendored Sophie lists before they reach the INI. # Explicit and reasoned so that re-vendoring a newer preset cannot silently # reintroduce something we deliberately dropped. zomboid_mods_excluded: # Already absent from the 2026-07-31 preset; listed so it stays that way. - workshop_id: "3718412967" mod_id: IconsInventory reason: Sophie recommends removing Icon Inventory # Build 41 mod (versionMin=41.60) that indexes the ModOptions framework, which # Sophie's list does not include. Dies at VehicleDoorsHotkey_Options.lua:4 -- # "attempted index: ModOptions of non-table: null" -- and never initialises, so # it is 21 exceptions a boot in exchange for a hotkey that cannot work. - workshop_id: "2290459371" mod_id: VehicleDoorsHotkey reason: needs the absent ModOptions framework; throws on load and never runs # Per-source-IP ceiling on 53-byte Steam/PZ query packets before they are # logged and dropped. Set well above anything a real client produces -- opening # the server browser or retrying a connection is a handful of queries, while a # scanner flood is thousands. The previous 1/hour burst 2 was below normal play # and made the server unreachable. See containers/home/zomboid.yml. zomboid_query_rate_limit: 60/minute zomboid_query_burst: 120 # How long to keep PZ's own log archive under data/Logs. # # PZ rolls logs into dated directories and never prunes them; logrotate cannot # bound that, because the growth is in the number of files rather than the size # of any one of them. See containers/home/zomboid.yml. zomboid_log_retention_days: 14 # Mod IDs the Sophie preset's `Mods=` line gets wrong for a 42.20.3 server. # # Every workshop item below is in the preset's own WorkshopItems= list and # downloads fine -- only the ID it is referenced by is stale, so the server # logs `required mod ... not found` and quietly runs without it. Verified # against the id= field of each mod.info on disk, not guessed from folder # names (folder name and mod ID are unrelated). Applied by server.ini.j2 so a # re-vendor of a newer preset keeps the corrections. zomboid_mods_renamed: - old: "FWOBenchPress&Treadmill" new: FWOBenchPressTreadmill reason: preset carries a stray ampersand; mod.info in 2940354599 has none - old: Ladders42131 new: Ladders4220 reason: 3629835761 ships per-build variants; 42131 is the 42.16 one, 4220 is ours - old: ServingPlatesB42 new: ServingPlates42 reason: mod.info in 3399320470 declares ServingPlates42 - old: NewMusic_OrchestraMix new: NewMusic_CMM reason: author renamed it to "Classical Music Mix" in 3760471726 # Server config is seeded once and then left alone, so it can be hand-edited # between world regenerations. Set true to overwrite it from the templates. zomboid_config_force: false # Same switch, narrowed to spawnregions.lua and spawnpoints.lua. They are the # only config a running world re-reads (at every server start, unlike # SandboxVars, which is read only when the world is created), so a spawn edit # can be deployed to the live world without a wipe. It is separate from # zomboid_config_force so that landing one is not an excuse to force the INI and # SandboxVars over the top of whatever the admins have set in-game. # # make deploy TAGS=zomboid-conf -e zomboid_spawn_force=true # # The server still has to be restarted, with players offline, before the new # spawn config is read. zomboid_spawn_force: false sshpass_cron_path: "{{ podman_volumes }}/sshpass_cron" caddy_path: "{{ podman_volumes }}/caddy" # Drone CI variables removed - infrastructure decommissioned # drone_server_proto, drone_runner_proto, drone_runner_capacity # Server names (used by Caddy) base_server_name: bdebyl.net assistant_server_name: assistant.bdebyl.net bookstack_server_name: wiki.skudakrennsport.com # ci_server_name: removed - Drone CI decommissioned cloud_server_name: cloud.bdebyl.net cloud_skudak_server_name: cloud.skudakrennsport.com fulfillr_server_name: fulfillr.debyltech.com home_server_name: home.debyl.io uptime_kuma_server_name: uptime.debyltech.com uptime_kuma_personal_server_name: uptime.debyl.io parts_server_name: parts.bdebyl.net photos_server_name: photos.bdebyl.net # debyl.io domains (migration from bdebyl.net) base_server_name_io: debyl.io assistant_server_name_io: assistant.debyl.io cloud_server_name_io: cloud.debyl.io home_server_name_io: home.debyl.io parts_server_name_io: parts.debyl.io photos_server_name_io: photos.debyl.io gitea_debyl_server_name: git.debyl.io rsvp_server_name: rsvp.debyl.io # skudak.com domains (migration from skudakrennsport.com) parts_skudak_server_name: parts.skudak.com bookstack_server_name_new: wiki.skudak.com # partsy_skudak_admin_password: defined in vault cloud_skudak_server_name_new: cloud.skudak.com gitea_skudak_server_name: git.skudak.com # LibreSign root certificate authority identity for skudak-cloud. This is the # issuer name that appears on every signed document, so it must match the # entity's legal name -- it was previously generated as "Skudak Rennsport LLP", # the pre-rename name. Changing these values does NOT re-issue the CA on its # own; see the guarded generate task in containers/skudak/cloud.yml. # Skudak brand palette, mirroring ~/src/skudak/skudak-site/src/styles/variables.css. # --color-gray-900 for the mail header band and UI chrome; the white signature # logo is legible on it. --color-accent is spent only on the CTA button, and # lives in the skudakmail app rather than here since theming has no second # colour slot. theming_skudak_primary: "#0A0A0A" libresign_skudak_cert_cn: Skudak LLP libresign_skudak_cert_o: Skudak LLP libresign_skudak_cert_c: US libresign_skudak_cert_st: New Hampshire libresign_skudak_cert_l: Newbury # Legacy nginx/ModSecurity configuration removed - Caddy provides built-in security # Web server configuration (Caddy is the default) # Legacy nginx variables kept for cleanup tasks # Caddy configuration caddy_email: "{{ ssl_email }}" # Use staging for testing, production for real certificates caddy_acme_ca: https://acme-v02.api.letsencrypt.org/directory # For testing/staging: # caddy_acme_ca: https://acme-staging-v02.api.letsencrypt.org/directory # Caddy ports caddy_admin_port: 2019 # Caddy network configuration caddy_local_networks: - 192.168.0.0/16 - 127.0.0.1 # Caddy logging configuration caddy_log_level: INFO caddy_log_format: json # Log rotation. Caddy rotates on implicit defaults (100MiB / keep 10 / 90d) # without these, and those were demonstrably not holding: 20 rotated files per # stream for the busiest logs and a 190-day-old .gz, for 1.3 GB across 16 # streams. Kept short deliberately -- fluent-bit tails every one of these into # Graylog (see roles/common/templates/fluent-bit/fluent-bit.conf.j2), so # Graylog is the system of record and the local files are only a buffer. caddy_log_roll_size: 10MiB caddy_log_roll_keep: 3 caddy_log_roll_keep_for: 168h # Caddy performance tuning caddy_max_request_body_mb: 500 # Caddy security headers (global defaults) caddy_security_headers: Strict-Transport-Security: "max-age=31536000; includeSubDomains" X-Content-Type-Options: "nosniff" Referrer-Policy: "same-origin" X-Frame-Options: "SAMEORIGIN" # Graylog logging stack graylog_path: "{{ podman_volumes }}/graylog" logs_server_name: logs.debyl.io # gelf_auth_token: defined in vault - X-Gelf-Token header for Lambda GELF HTTP auth # Fluent Bit is deployed as a systemd service (not container) # for direct journal access - see containers/base/fluent-bit.yml # Fluent-bit Caddy log forwarding caddy_log_path: "{{ caddy_path }}/logs" caddy_log_names: - caddy - photos - wiki - assistant - parts - uptime-kuma - uptime-kuma-personal - graylog - cloud - cloud-skudak - gitea-debyl - gitea-skudak - fulfillr # GeoIP configuration for Graylog # Requires free MaxMind account: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data geoip_path: "{{ graylog_path }}/geoip" geoip_database_edition: GeoLite2-City # geoip_maxmind_account_id: defined in vault # geoip_maxmind_license_key: defined in vault # Weekly podman prune (see templates/podman-prune.sh.j2). Both rootless users # have their own image store; the git user runs the Gitea pods. podman_prune_users: - "{{ podman_user }}" - "{{ git_user }}" # Keep 30 days of unused images so a rollback needs no rebuild or re-pull. podman_prune_until: 720h # The CI runners were never pruned and had run away: gitea-runner reached 1205 # images / 113 GB with 100% reclaimable, actions-runner 137 exited job # containers. Build layers carry no rollback value, so they keep a far shorter # window than the service stores and their exited containers are reaped too. podman_prune_ci_users: - gitea-runner - actions-runner podman_prune_ci_until: 48h # CI base images built by roles/gitea-actions carry this label (gitea_ci_keep_label # there -- keep the two in sync) and are skipped by the CI image prune. podman_prune_ci_keep_label: io.debyl.ci-base # Daily rather than weekly: CI turns over many images a day, and a week of that # is what let the store reach 113 GB between runs. podman_prune_oncalendar: "*-*-* 02:00:00" # Hardened CIFS options for the TrueNAS shares (see containers/home/photos.yml). # x-systemd.automount is what makes a failed mount recoverable without a human. cifs_mount_opts: >- credentials=/etc/cifs/photos.creds,uid={{ podman_subuid.stdout }},gid={{ podman_subuid.stdout }},_netdev,nofail,soft,x-systemd.automount,x-systemd.mount-timeout=30,x-systemd.idle-timeout=600,vers=3.1.1,resilienthandles,echo_interval=10 # How often the watchdog checks mount health and heals immich. cifs_watchdog_oncalendar: "*:0/5" cifs_watchdog_mounts: - "{{ photos_path }}/storage" - "{{ photos_path }}/immich"