Files
VacatureRadar/docs/operations/UNRAID_DEPLOYMENT.md
T
NuklearRabbit f20b7de053
Managed validation / full (pull_request) Successful in 3m1s
hygiene: prepare VacatureRadar for public release
2026-09-02 23:37:30 +02:00

234 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
<projectmap>/.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 server.example.test
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/<ts>/artifacts/release-smoke.json`
- `release-artifacts/<ts>/artifacts/smoke-restored-validate.json`
- `release-artifacts/<ts>/artifacts/upgrade-rollback.md`
- `release-artifacts/<ts>/artifacts/checksums.txt`
- `release-artifacts/<ts>/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=<fernet-key>
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:<versie>@sha256:<digest>` 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.