Files
deploy_home/ansible/roles/podman/defaults/main.yml
T
Bastian de BylandClaude Opus 5.5 fa5bbf8e54 feat(debyltech-cloud): add cloud.debyltech.com Nextcloud
A de Byl Technologies LLC Nextcloud cloned from the Skudak instance:
LibreSign signing for people without an account, registration off
(admin-created accounts only), no Group Folders. DNS is a terraform-managed
ALIAS to fulfillr.debyltech.com.

- containers/debyltech/cloud.yml: nextcloud/mariadb/redis on port 8091.
  It installs unattended on the first deploy, sends mail through SES as
  noreply@debyltech.com, and re-asserts the Skudak LibreSign settings.
- files/debyltechmail: skudakmail rebranded, with a new black-and-white
  wordmark and white web-UI logos.
- LibreSign is pinned to 14.2.2 from the GitHub release (sha256-checked)
  rather than `occ app:install`. The app store served a same-day 14.2.3
  whose tarball has no binary-signature metadata. 14.2.x also doesn't
  create its own download dirs, so they're pre-created.
- The backup runs nightly at 04:15 to TrueNAS /mnt/glacier/debyltechcloud and
  reaches personal iDrive via the "iDrive E2 Backup" task; the TrueNAS side
  excludes /debyltechcloud/_backup/config/**.
- Fix the libresign:configure:check gate in both instances: '\berror\b'
  becomes a backspace in Jinja and never matched, so a check reporting three
  errors passed clean. Now '\\berror\\b'.
- vault: cloud_debyltech_* secrets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 17:04:04 -04:00

365 lines
16 KiB
YAML

---
# 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"
cloud_debyltech_path: "{{ podman_volumes }}/debyltechcloud"
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/<name>.ini,
# Saves/Multiplayer/<name>/ and db/<name>.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
# de Byl Technologies Nextcloud (containers/debyltech/cloud.yml). DNS is a
# Route53 ALIAS to fulfillr.debyltech.com, managed in ~/src/debyltech/terraform
# (aws/cloud.tf), so no awsddns container of its own.
cloud_debyltech_server_name: cloud.debyltech.com
# debyltech-com $primary-color (copper). Drives the web UI theming; the mail
# CTA carries the same value as a constant in files/debyltechmail.
theming_debyltech_primary: "#bc804d"
# Login/header background. Dark site ink rather than copper so the white
# wordmark and mark shipped in files/debyltechmail/img stay legible on it.
theming_debyltech_background: "#0a1a2b"
# LibreSign root CA identity: the issuer on every signed document. Same caveat
# as the Skudak block above -- changing these does not re-issue the CA.
libresign_debyltech_cert_cn: de Byl Technologies LLC
libresign_debyltech_cert_o: de Byl Technologies LLC
libresign_debyltech_cert_c: US
libresign_debyltech_cert_st: New Hampshire
libresign_debyltech_cert_l: Newbury
# Outbound mail via AWS SES SMTP as noreply@debyltech.com. The IAM user is
# NextcloudSMTP in the terraform repo; its SMTP username/password are
# cloud_debyltech_smtp_user / cloud_debyltech_smtp_pass in the vault.
cloud_debyltech_smtp_host: email-smtp.us-east-1.amazonaws.com
cloud_debyltech_smtp_port: 465
# 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
- cloud-debyltech
- 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
# The CI base images declare LABEL io.debyl.ci-base="true" in
# roles/gitea-actions/files/Containerfile.* (and .gitea/workflows/ci-images.yml
# verifies it before publishing -- keep all three in sync). Images carrying it
# are skipped below: `until` counts from build time and not pull time, so
# without the exemption a re-pulled image would be deleted again the next
# night, a 7.8 GB ESP-IDF re-download after every idle day. Superseded tags
# (e.g. after an esp_idf_version bump) survive too; remove those by hand.
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"
# GA4 property for the fulfillr portal Traffic & funnel tab (SCRUM-196, non-secret).
# Shared by dev and prod. The service-account key `fulfillr_ga4_credentials_json` lives in the vault.
fulfillr_ga4_property_id: "353859448"