# 117 — Radicale Deployment Guide > Status: **NOT YET DEPLOYED** — planning reference only. Do not treat CT 117 or this config as live until each step is physically completed and confirmed. **Service:** Radicale (lightweight CalDAV/CardDAV server) **CT ID:** 117 **IP:** 192.168.1.117 **Domain:** cal.spendlik.sk **Container type:** Unprivileged LXC, Debian 13 (trixie) — no Docker required, Radicale is a single Python package **Auth:** Authelia (CT 102) in front, plus Radicale's own htpasswd file as a second layer (CalDAV clients need Basic Auth to authenticate directly — Authelia SSO alone doesn't work well with CalDAV client protocols) --- ## Phase overview 1. Create LXC container 2. Install Radicale + dependencies 3. Configure Radicale (storage path, users, rights) 4. Create systemd service 5. nginx reverse proxy (CT 101) 6. DNS — WebSupport A record (both management pages) + DDNS updater (CT 108) 7. Let's Encrypt SSL via certbot + manual nginx config inspection 8. Authelia bypass/config decision 9. Create first calendar + test with a CalDAV client 10. Client setup — Thunderbird (CachyOS) + DAVx⁵ (Galaxy S25) 11. Update `00_index.md` and `homelab-overview.md` — **only after physical deployment** --- ## Phase 1 — Create LXC container ```bash pct create 117 local:vztmpl/debian-13-standard_13.0-1_amd64.tar.zst \ --hostname radicale \ --cores 1 \ --memory 256 \ --net0 name=eth0,bridge=vmbr0,ip=192.168.1.117/24,gw=192.168.1.1 \ --unprivileged 1 \ --features nesting=0 \ --storage local-lvm \ --rootfs local-lvm:4 ``` - 256MB RAM / 4GB disk is generous for Radicale — it's a single-user Python app with a flat-file backend - `nesting=0` — no Docker needed, so no privileged container required, unlike your other services ## Phase 2 — Install Radicale ```bash pct start 117 pct enter 117 apt update && apt install -y python3-pip python3-venv nano python3 -m venv /opt/radicale source /opt/radicale/bin/activate pip install radicale ``` - `nano` installed per your standing convention for new containers ## Phase 3 — Configure Radicale ```bash mkdir -p /etc/radicale /var/lib/radicale/collections htpasswd -c /etc/radicale/users spendlik ``` `/etc/radicale/config`: ```ini [server] hosts = 0.0.0.0:5232 [auth] type = htpasswd htpasswd_filename = /etc/radicale/users htpasswd_encryption = bcrypt [storage] filesystem_folder = /var/lib/radicale/collections [rights] type = owner_only ``` - `owner_only` — each authenticated user only sees their own collections; fine for single-user use, revisit if you ever add a second account ## Phase 4 — systemd service `/etc/systemd/system/radicale.service`: ```ini [Unit] Description=Radicale CalDAV/CardDAV server After=network.target [Service] ExecStart=/opt/radicale/bin/radicale --config /etc/radicale/config Restart=on-failure User=radicale [Install] WantedBy=multi-user.target ``` ```bash useradd -r -s /usr/sbin/nologin radicale chown -R radicale:radicale /var/lib/radicale /etc/radicale systemctl daemon-reload systemctl enable --now radicale systemctl status radicale ``` ## Phase 5 — nginx reverse proxy (CT 101) New vhost, HTTP-only first (per your certbot gotcha — always issue on a working HTTP config before adding SSL): ```nginx server { listen 80; server_name cal.spendlik.sk; location / { proxy_pass http://192.168.1.117:5232/; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Script-Name /; } } ``` ## Phase 6 — DNS - Add `cal.spendlik.sk` A record in **both** WebSupport management pages ⚠️ - Add `cal.spendlik.sk` to the DDNS updater script in CT 108, with record ID once created - Update `00_index.md` WebSupport DNS Record ID table — after the record actually exists ## Phase 7 — SSL ```bash certbot --nginx -d cal.spendlik.sk ``` - ⚠️ Manually inspect the resulting nginx config afterward — check for duplicate `server_name` and missing closing braces (known recurring certbot issue in this environment) ## Phase 8 — Authelia CalDAV clients (Thunderbird, DAVx⁵) authenticate via HTTP Basic Auth on every request — they don't handle browser-based SSO redirects well. Recommended: **bypass Authelia entirely for `cal.spendlik.sk`** and rely on Radicale's own htpasswd auth + HTTPS. Add the bypass rule in CT 102's Authelia config, ordered before the catch-all 2FA rule (same pattern as your n8n `/api/*` bypass). ## Phase 9 — Create calendar + test Radicale auto-creates collections on first client connection, or manually: ```bash mkdir -p /var/lib/radicale/collections/spendlik/personal chown -R radicale:radicale /var/lib/radicale/collections/spendlik ``` Test with `curl` first: ```bash curl -u spendlik https://cal.spendlik.sk/spendlik/personal/ ``` ## Phase 10 — Clients **CachyOS (Thunderbird):** - Calendar → New Calendar → On the Network → CalDAV - URL: `https://cal.spendlik.sk/spendlik/personal/` **Galaxy S25 (DAVx⁵):** - Add account → base URL `https://cal.spendlik.sk/spendlik/` - Syncs to native Samsung Calendar app once configured ## Phase 11 — Documentation (after physical deployment only) - Add CT 117 row to `00_index.md` inventory table - Add `cal.spendlik.sk` DNS record ID once known - Add `117_radicale_deployment.md` to Documentation Map - Move Radicale from "Active Projects" to deployed state - Update `homelab-overview.md` container table --- ## Notes - No Docker, no privileged container — smallest-footprint service in the stack - Backup: `/var/lib/radicale/collections` is the only stateful data — include in existing LXC nightly backup job, no special handling needed