# Unraid-deployment Deze handleiding gebruikt `docker-compose.unraid.yml` als declaratieve bron van waarheid. De Unraid-variant bundelt web, worker, scheduler, PostgreSQL en Redis bewust in één Dockerman-container met Supervisor als procesbewaker. De gewone `docker-compose.yml` behoudt de gescheiden productieprocessen voor andere hosts. ## Vooraf Alle paden in `docker-compose.unraid.yml` zijn relatief aan de map waarin dat bestand staat. Je kopieert de projectmap dus naar een plek naar keuze; de aanbevolen locatie blijft: ```text /mnt/user/appdata/VacatureRadar ``` De container maakt `local/`, `local/media`, `local/logs`, `local/postgres-aio17`, `local/redis-aio` en `local/backups` bij de eerste start zelf aan en zet de juiste eigenaar. Voor back-ups blijft `/mnt/user/backups/vacatureradar` de aanbevolen doelmap. Gebruik bij voorkeur een cache-backed `appdata`-share en neem die afzonderlijk in je back-upregime op. De PostgreSQL-datadir mag niet gelijktijdig door file-syncsoftware worden gemuteerd. ## Manueel kopiëren en uitrollen Wanneer je niet via `scripts/deploy_unraid.sh` werkt maar de map met de hand kopieert (rsync, SMB-share, ZIP): ```bash cd /mnt/user/appdata/VacatureRadar cp .env.unraid.example .env # alleen de eerste keer nano .env # alle CHANGE_ME-waarden vervangen docker compose -f docker-compose.unraid.yml up -d --build --force-recreate docker compose -f docker-compose.unraid.yml logs -f ``` Kopieer `local/` niet mee vanaf je werkstation: die map bevat de databasedirectory van de server. Kopieer `.env` evenmin over een bestaand serverbestand heen. Een afwijkende hostpoort zet je in `.env` via `VACATURERADAR_HOST_PORT`; standaard is dat `1226`. ## Dockerman De container verschijnt als **VacatureRadar** en bevat metadata voor: - WebUI: `http://[IP]:1226/`; - icoon: het lokale 256×256 VacatureRadar-PNG op `/static/img/vacatureradar-docker.png`; - één herstart- en healthcheckpunt voor alle interne processen. Dockerman haalt het tegelicoon via de gepubliceerde webpoort op en cachet het. De PNG is daarom een afzonderlijke rasterasset met een stabiel pad; de SVG-favicon blijft voor browsers behouden. Het icoonlabel gebruikt bewust de concrete server-side URL `http://127.0.0.1:1226/...`: Dockerman vervangt `[IP]` en `[PORT:...]` alleen voor de WebUI, niet voordat het een icoon downloadt. Na een upgrade waarbij het oude icoon nog zichtbaar is, herlaad de Docker-pagina zodat Dockerman de lokale cache opbouwt. ## Imagekeuze De Unraid-compose bouwt lokaal met `Dockerfile.unraid` en tagt de image als `vacatureradar:unraid`. Pin voor reproduceerbare releases aanvullend een commitgebonden tag. 1. bouw lokaal op de Unraid-host en vervang `image:` door een vaste lokale tag; 2. publiceer vanuit CI naar een private registry en pin op een immutable digest; 3. gebruik `build:`, mits de gekozen Compose-plugin builds betrouwbaar ondersteunt. Gebruik in productie geen zwevende `latest` zonder gecontroleerd rollbackpad. ## Configuratiebestand `docker-compose.unraid.yml` leest `.env` naast het composebestand zelf: ```text /.env ``` Een afwijkende locatie kan vóór Compose via `VACATURERADAR_ENV_FILE` worden ingesteld. Start van [`.env.unraid.example`](../../.env.unraid.example) voor een all-in-one container die rechtstreeks op poort 1226 draait, of van [`deployment/production.env.example`](../../deployment/production.env.example) zodra er een reverse proxy met TLS voor staat. Kopieer nooit ongewijzigde `CHANGE_ME`-waarden. Configureer een bestaand bestand voor de publieke URL zonder secrets te overschrijven: ```bash cd /mnt/user/appdata/VacatureRadar python scripts/configure_public_url.py \ https://vacatureradar.example.be \ --env-file .env \ --cache-url redis://127.0.0.1:6379/1 ``` Beperk de bestandsrechten van `.env` tot de beheerder. Voeg geen secrets toe aan de ZIP, Git, screenshots of supportlogs. ## Eerste uitrol 1. Controleer alle `CHANGE_ME`-waarden en maak een back-up van een bestaande `.env`. 2. Start of hermaak de Unraid-container vanuit de bronmap: ```bash cd /mnt/user/appdata/VacatureRadar docker compose -f docker-compose.unraid.yml up -d --build --force-recreate ``` 3. De entrypoint valideert de productieconfiguratie, initialiseert PostgreSQL, voert migraties uit en start alle processen onder Supervisor. 4. Controleer intern `GET /health/live/` en `GET /health/ready/`. 5. Voer alleen wanneer bootstrap niet automatisch is ingeschakeld eenmalig uit: ```bash docker exec -it VacatureRadar python manage.py bootstrap_instance ``` 6. Meld lokaal aan en wijzig het bootstrapwachtwoord. 7. Activeer nog geen live bron of mailbox voordat bronbeleid, retentie en back-up zijn gecontroleerd. ## Beheerverbinding en heruitrol Gebruik vanaf een beheerwerkstation altijd een benoemde SSH-hostalias met een afzonderlijke deploysleutel. Een rechtstreekse verbinding naar het IP-adres kan de lokale standaardgebruiker en standaardsleutel kiezen en daardoor ten onrechte als een server-side authenticatiefout lijken. ```sshconfig Host unraid-itworx HostName 192.168.10.150 Port 22 User root IdentityFile ~/.ssh/itworx_unraid_deploy IdentitiesOnly yes ``` Controleer de verbinding zonder wachtwoordprompt en voer daarna de beveiligde synchronisatie, imagebuild, containervervanging en health-smoke uit: ```bash ssh -o BatchMode=yes unraid-itworx 'id -un && docker info >/dev/null' bash scripts/deploy_unraid.sh ``` `deploy_unraid.sh` weigert een IP-adres, niet-rootprofiel of generieke standaardsleutel. Alleen door Git gekende of niet-genegeerde projectbestanden worden verpakt; `.env`, `local/`, databasebestanden en andere genegeerde runtimegegevens worden niet verzonden. De helper wacht op Docker-health en controleert `/health/ready/` voordat hij succes meldt. Een afwijkende veilige alias kan expliciet via `UNRAID_SSH_HOST` worden ingesteld. ## Releaseverificatie en upgradepad VR-117 voegt `./scripts/release_verify.sh` toe als release-check met artifacts voor package, checksum, configdiff, sbom en upgrade/rollback. Het script bewaart: - `release-artifacts//artifacts/release-smoke.json` - `release-artifacts//artifacts/smoke-restored-validate.json` - `release-artifacts//artifacts/upgrade-rollback.md` - `release-artifacts//artifacts/checksums.txt` - `release-artifacts//artifacts/config-diff.txt` De restorecontrole vergelijkt primaire en gerestoreerde modelcounts in beide smoke-rapporten. ## Reverse proxy en TLS Expose de applicatie niet rechtstreeks op internet. Plaats Nginx Proxy Manager, Traefik, Caddy of een gelijkwaardig beheerd reverse-proxyprofiel voor poort 1226. Vereisten: - geldig TLS-certificaat; - alleen HTTPS extern; - `X-Forwarded-Proto` correct doorgeven; - request-bodylimiet klein houden; er is geen algemene upload-API; - optioneel extra access control/VPN voor persoonlijke installatie; - geen publieke toegang tot PostgreSQL, Redis of Ollama. Wanneer een proxy op hetzelfde Docker-netwerk draait, publiceer poort 1226 alleen intern. Wanneer Unraid routing een hostpoort vereist, beperk die via firewall/VLAN tot de proxy of het beheernetwerk. Volg voor host-, CSRF-, proxyheader- en TLS-instellingen de [Nginx Proxy Manager-handleiding](NGINX_PROXY_MANAGER.md). ## Mailbox Gebruik per vacatureplatform een aparte mailbox of alias, niet de hoofdmailbox. Genereer eerst buiten Git een Fernet-sleutel: ```bash python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" ``` Plaats de uitvoer uitsluitend in de Unraid-secretconfiguratie: ```dotenv MAILBOX_CREDENTIAL_KEYS= IMAP_CONNECT_TIMEOUT_SECONDS=15 IMAP_MAX_MESSAGES_PER_POLL=200 IMAP_MAX_MESSAGE_BYTES=1000000 ``` Herstart web, worker en scheduler. Open daarna **Bronnen → Platformmailboxen** en koppel ieder gewenst platform afzonderlijk: VDAB, Indeed, LinkedIn, ictjob.be, Jobat, StepStone, Careerjet, Randstad of Robert Half. Gmail en aangepaste publieke IMAP-hosts gebruiken een app-wachtwoord. Outlook / Microsoft 365 gebruikt centraal app-only OAuth via de vier `M365_*`-waarden en `EMAIL_BACKEND=apps.notifications.backends.Microsoft365OAuthEmailBackend`; geef de Entra-app alleen `IMAP.AccessAsApp` en `SMTP.SendAsApp` en beperk de Exchange-serviceprincipal tot de bedoelde mailbox. Laat `INBOX` geselecteerd tenzij de alert naar een aparte map wordt gefilterd. De scheduler controleert iedere vijf minuten welke mailbox volgens haar eigen interval verschuldigd is. Voor een immutable registryrelease zet je `VACATURERADAR_IMAGE=gitea.itworx.tech/jens/vacatureradar:@sha256:` en start je met `docker compose -f docker-compose.unraid.yml up -d --no-build`. ## Docker-netwerkcapaciteit Deze Unraid-host draait veel geïsoleerde Compose-netwerken. De ingebouwde Dockerpools delen grote `/16`- en `/20`-netwerken uit en kunnen daardoor opraken terwijl er nog ruim voldoende private adresruimte bestaat. De live host gebruikt daarom persistent in `/boot/config/docker.cfg`: ```text DOCKER_OPTS="--default-address-pool base=10.200.0.0/16,size=24" ``` Dat levert 256 nieuwe `/24`-netwerken en overlapt niet met LAN `192.168.10.0/24`, WireGuard `10.253.0.0/16` of bestaande Docker-netwerken. De instelling geldt alleen voor nieuw aangemaakte netwerken; bestaande subnets veranderen niet. Maak vóór wijziging een kopie van `docker.cfg`, herstart Docker éénmaal en bewijs de pool met een tijdelijk netwerk dat na inspectie meteen wordt verwijderd. Voor sleutelrotatie zet je `nieuwe-sleutel,oude-sleutel` in `MAILBOX_CREDENTIAL_KEYS`, herstart je de processen en draai je: ```bash python manage.py rotate_mailbox_credentials ``` Verwijder de oude sleutel pas na een geslaagde sync. Een rollback van migratie `0005` verwijdert de mailboxkoppelingen; maak daarom eerst een versleutelde databaseback-up en pauzeer de scheduler. ## Ollama Ollama is optioneel. Start de composeprofile pas wanneer de deterministische kern goed werkt: ```bash docker compose -f docker-compose.unraid.yml --profile ai up -d ``` Pin een modelnaam in `OLLAMA_MODEL`; zonder model blijft `OLLAMA_ENABLED=0`. Stel Ollama niet extern bloot. Modeloutput wordt als onbetrouwbare gestructureerde data behandeld en mag geen harde regel of sollicitatieactie uitvoeren. ## Upgraden 1. Maak en verifieer een database- en configuratieback-up. 2. Lees `CHANGELOG.md` en de releaseartefacten. 3. Als de release nog niet geverifieerd is, voer `./scripts/release_verify.sh` lokaal uit. 4. Pull/bouw de nieuwe immutable image. 5. Stop de appcontainer. 6. Start de nieuwe appcontainer en laat de entrypoint migraties uitvoeren. 7. Controleer health, login en release-smoke-rapport. 8. Controleer in de containerlogs dat web, worker en scheduler actief zijn. 9. Bewaar de vorige image/digest totdat één volledige schedulercyclus goed verliep. ## Rollback Bij een codefout zonder incompatibele migratie: pin de vorige image en herstart web/worker/scheduler. Bij een incompatibele datamigratie: stop alle schrijvers, herstel de vooraf gemaakte databaseback-up en gebruik de overeenkomende vorige image. Voer nooit een restore uit terwijl de worker of scheduler schrijft. ## Productiechecklist - [ ] alle `CHANGE_ME`-waarden verwijderd; - [ ] debug uit en secure cookies aan; - [ ] TLS en host/origin exact ingesteld; - [ ] PostgreSQL/Redis/Ollama niet publiek; - [ ] dagelijkse back-up plus periodieke hersteltest; - [ ] aparte IMAP-mailbox met minimale rechten; - [ ] bronpolicy per actieve bron op `allow` met reviewdatum; - [ ] adminwachtwoord gewijzigd; - [ ] healthmonitoring en vrije schijfruimtebewaking; - [ ] retentiejob en logrotatie gecontroleerd.