Privé-implementatie op afstand
Dit patroon voert Libre WebUI, Ollama en Cloudflare Tunnel uit op één Docker-host zonder de poorten van toepassing of Ollama te publiceren. Cloudflare Access is de buitenste identiteitsgrens; verificatie door Libre WebUI blijft de binnenste grens. Work en Watchtower zijn afzonderlijke, root-equivalente opties waarvoor je bewust kiest.
Dit sjabloon is de solo-topologie met één replica: SQLite, lokale versleutelde blobs, ingebouwde vectoren, lokale coördinatie en een ingebouwde taakworker delen het gegevensvolume van de toepassing. Maak hier geen teamimplementatie van door backendselectoren in .env te wijzigen. Teamimplementaties moeten docker-compose.team.yml uit de repository gebruiken (en docker-compose.team.work.yml wanneer Work is ingeschakeld). Deze leveren PostgreSQL/PGVector, S3-opslag met versies, Redis, een externe worker en de gateway als één gecoördineerde topologie.
Gebruik deploy/private/docker-compose.yml als uitgangspunt. Standaard wordt de image main gebruikt:
LIBRE_WEBUI_IMAGE=ghcr.io/libre-webui/libre-webui:main
De tag dev is geschikt voor een ontwikkelinstantie die daar expliciet voor kiest, niet als standaard voor een client.
Beveiligingsmodel
- Cloudflare Access beschermt de volledige hostnaam, waaronder
/api/*en WebSocket-upgrades. Voeg geen openbare omzeilingspaden toe. - Libre WebUI vereist een huidig account voor toepassings-API's. De levenscyclus van modellen en Work-bewerkingen vereisen dat de huidige databaserol beheerder is.
- De toepassing, Ollama, SearXNG en cloudflared gebruiken alleen een privé-Compose-netwerk. De host publiceert geen toepassingspoorten.
- De meegeleverde SearXNG-service ondersteunt optioneel zoeken op het web. Deze is alleen intern en inactief totdat een beheerder zoeken inschakelt via Instellingen > Zoeken; stel
SEARXNG_SECRETin.envin voordat je de stack start. - De app draait zonder rootrechten met een alleen-lezen rootbestandssysteem, zonder Linux-mogelijkheden, met no-new-privileges en limieten voor CPU, geheugen en PID's.
- Work is uitgeschakeld tenzij een van de overschrijvingen wordt opgenomen. Indien ingeschakeld voegen de containers een eigen alleen-lezen rootbestandssysteem, verwijderde mogelijkheden, resourcelimieten, werkruimtevolume en standaard-weigeren-netwerkbeleid toe.
De basisstack koppelt geen Docker-socket. Work inschakelen met docker-compose.work-proxy.yml behoudt dat: een socketproxy op een intern netwerk houdt de socket vast en stuurt alleen de API-gedeelten door die Work gebruikt (containers, images, volumes, netwerken, exec, info); swarm, secrets, build en systeemeindpunten worden door de proxy geweigerd en de toepassing heeft geen socketmount of lidmaatschap van de socketgroep nodig. De proxy verkleint het Docker API-oppervlak, niet de impact van wat wordt doorgestuurd — wie containers kan maken, kan nog steeds hostpaden bind-mounten. Behandel dit als echte beveiligingslaag, niet als multi-tenantisolatie.
De alternatieven met onbewerkte socket blijven de grootste vertrouwensgrens: de overschrijvingen docker-compose.work.yml en Watchtower geven een container een proces dat willekeurige Docker API-aanroepen kan doen en daarmee de host kan besturen. Een alleen-lezen socketmount maakt API-toegang niet alleen-lezen. De geïntegreerde back-uphelper weigert een onbewerkte Docker-socket te erven; migreer Work naar de gefilterde proxy voordat je op geplande geïntegreerde back-ups vertrouwt.
Eerste installatie
- Maak een sudo-operator zonder rootrechten en controleer aanmelden via SSH-sleutel voordat je root-SSH uitschakelt.
- Kopieer
deploy/private/.env.examplenaar/opt/libre-webui/.env, stel modus0600in, genereer unieke geheimen en stemBLOB_QUOTA_BYTES_PER_USERaf op de host.BLOB_QUOTA_RESERVATION_TTL_MSlaat verlaten uploadreserveringen vervallen; standaard na één uur. - Stel als Work wordt ingeschakeld
DOCKER_GIDin op de numerieke groep die eigenaar is van/var/run/docker.sock. - Bewaar het Cloudflare-tunneltoken in
/opt/libre-webui/secrets/tunnel-tokenmet modus0640of strenger. - Maak een zelfgehoste Cloudflare Access-toepassing voor de volledige hostnaam, gebruik een sessie van 24 uur en sta alleen bedoelde identiteiten toe. Schakel Protect with Access in op de tunnelroute. Als monitoring een openbare gezondheidscontrole vereist, maak dan een afzonderlijke toepassing of policy die alleen op
/health/liveis gericht. Voeg nooit een algemene Bypass-policy aan de hoofdtoepassing toe: overeenkomende Bypass-policy's maken de Allow-policy ongedaan. - Laat
ENABLE_SIGNUP=false. Maak de eerste lokale beheerder nadat de Access-toegestane lijst de hostnaam beschermt; een lege database staat dat ene bootstrapaccount automatisch toe. Schakel registratie alleen in tijdens een bewust gekozen later registratievenster. - Configureer hostnaamrestricties voor Turnstile en stel
TURNSTILE_EXPECTED_HOSTNAMEin op de exacte openbare hostnaam.
Start en controleer:
cd /opt/libre-webui
docker compose config --quiet
docker compose up -d
docker compose ps
Neem bewust de socketproxy-overschrijving op om Work in te schakelen:
docker compose -f docker-compose.yml -f docker-compose.work-proxy.yml up -d
De variant met onbewerkte socket (docker-compose.work.yml) blijft beschikbaar voor implementaties die deze nodig hebben, met de hierboven beschreven vertrouwensgevolgen.
Zodra Access actief is, hebben opdrachtregelcontroles een Cloudflare Access-servicetoken nodig, tenzij het exacte pad een nauwe omzeiling heeft. Bewaar de referenties buiten de shellgeschiedenis en stuur beide headers:
curl --fail --silent --show-error \
-H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
-H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET" \
https://your-hostname.example/api/auth/system-info
Een niet-geverifieerd verzoek aan een beschermde toepassings-API moet 401 retourneren:
curl --output /dev/null --write-out '%{http_code}\n' \
-H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
-H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET" \
https://your-hostname.example/api/work/tasks
Hostbeveiliging
De map bevat een sshd-drop-in en fail2ban-jail. Controleer vóór toepassing van de sshd-drop-in een aparte sudo-sessie zonder rootrechten in een andere terminal. Test de configuratie met sshd -t voordat je SSH herlaadt.
Gebruik UFW (of een vergelijkbare firewall) om inkomend verkeer standaard te weigeren en alleen begrensde SSH toe te staan. Docker publiceert in dit sjabloon geen servicepoorten:
ufw default deny incoming
ufw default allow outgoing
ufw limit OpenSSH
ufw enable
Houd onbeheerde beveiligingsupdates ingeschakeld. Schakel X11-, agent- en TCP-forwarding uit, tenzij de implementatie daar een gedocumenteerde reden voor heeft.
Back-ups en herstel
Voer vóór een back-up de alleen-lezen herstelinventaris uit in de actieve implementatiecontainer. Zo gebruik je de exact geïmplementeerde toepassingsversie, omgeving en het gekoppelde gegevensvolume. Een opdracht vanuit een checkout op de host kan de verkeerde database bekijken of broncode uitvoeren die afwijkt van de geïmplementeerde image.
docker exec libre-webui \
libre-webui recovery-check --json --data-dir /app/backend/data
Afsluitstatus 0 betekent dat geen blokkades voor herstelgereedheid zijn gevonden, 1 dat het JSON-rapport blokkades bevat en 2 dat de opdracht niet kon worden uitgevoerd. Het rapport bevat alleen een vingerafdruk van de versleutelingssleutel en vlaggen voor aanwezigheid van geheimen; het drukt nooit een sleutel of andere geheime waarde af. Bewaar de inventaris bij de bijbehorende back-up, zodat operators vóór herstel de toepassingsversie, schemavingerafdruk, verwachte Work-resources en uitsluitingen kunnen vergelijken.
Maak speciale versleutelings- en ondertekeningssleutels voor back-ups met de exact geïmplementeerde image. Houd deze map buiten het toepassingsvolume en kopieer versleutelingssleutel en persoonlijke ondertekeningssleutel naar een afzonderlijke beveiligde herstellocatie:
install -d -m 0700 /etc/libre-webui/backup-keys
image_ref=$(docker inspect libre-webui --format '{{.Image}}')
docker run --rm --user 0:0 --read-only --network none --cap-drop ALL \
--security-opt no-new-privileges \
--mount type=bind,src=/etc/libre-webui/backup-keys,dst=/backup-keys \
--entrypoint /usr/local/bin/libre-webui "$image_ref" \
backup keygen \
--directory /backup-keys
Sleutelgeneratie weigert bestaande uitvoerbestanden. Genereer nooit nieuwe sleutels over een bestaande back-upset: verlies van de archiefversleutelingssleutel of ondertekeningsidentiteit maakt het bijbehorende herstelbewijs onbruikbaar.
Installeer de meegeleverde back-up- en herstelscripts en systemd-units en schakel daarna de timer in:
install -d -m 0700 /var/backups/libre-webui
install -m 0750 deploy/private/libre-webui-backup \
/usr/local/sbin/libre-webui-backup
install -m 0750 deploy/private/libre-webui-restore \
/usr/local/sbin/libre-webui-restore
install -m 0644 deploy/private/libre-webui-backup.{service,timer} \
/etc/systemd/system/
systemctl daemon-reload
systemctl enable --now libre-webui-backup.timer
De unit leest optioneel alleen-voor-onderhoud-overschrijvingen uit /etc/libre-webui/backup.env; de .env van de toepassing wordt niet geladen. Maak het bestand als root alleen wanneer een overschrijving nodig is:
install -d -m 0750 /etc/libre-webui
install -m 0600 /dev/null /etc/libre-webui/backup.env
LIBRE_WEBUI_STACK_DIR, LIBRE_WEBUI_BACKUP_RETENTION_DAYS, LIBRE_WEBUI_CONTAINER_NAME en LIBRE_WEBUI_BACKUP_KEY_DIR kunnen daar rechtstreeks worden ingesteld. Houd het bestand eigendom van root en modus 0600. Een aangepaste sleutelmap moet binnen de systemd-sandbox leesbaar blijven voor root.
LIBRE_WEBUI_BACKUP_DIR wijzigen verandert ook de schrijfgrens van systemd. De map moet bestaan voordat de service start en de unit heeft een passende drop-in nodig. Bijvoorbeeld na LIBRE_WEBUI_BACKUP_DIR=/srv/backups/libre-webui in backup.env:
install -d -m 0700 /srv/backups/libre-webui
systemctl edit libre-webui-backup.service
Voeg dit exacte pad in de editor toe en herlaad de unit:
[Service]
ReadWritePaths=/srv/backups/libre-webui
systemctl daemon-reload
systemctl start libre-webui-backup.service
Zonder passende vermelding ReadWritePaths= voorkomt ProtectSystem=strict terecht dat de timer naar een aangepaste locatie schrijft.
De back-upservice staat maximaal zes uur toe voor grote archieven. De helper neemt een hostlock, stopt de toepassing alleen als deze al actief was en maakt het archief tegen het stilgelegde volume met de exact geïmplementeerde image. Het archief heeft een ondertekend manifest en een door de operator versleutelde payload; het bevat de gegevensmap plus runtime- en geheimconfiguratie die nodig zijn om die status te openen. De helper verifieert daarna onafhankelijk het volledige archief voordat het metagegevensrapport atomair wordt gepubliceerd. De alleen-lezen onderhoudscontainers krijgen een privé, schrijfbaar /tmp-tmpfs voor SQLite-inspectie en geverifieerde archiefcontrole; tijdelijke platte tekst blijft niet in de containerlaag staan. Kopieer beide bestanden en de afzonderlijk beveiligde herstelsleutels van de host af.
Wanneer Work docker-compose.work-proxy.yml gebruikt, moet herstel ook bewijzen dat elk Work-volume waarnaar de database verwijst nog bestaat. De helper leest DOCKER_HOST van de geïmplementeerde toepassing, vindt de socketproxyservice in hetzelfde actieve Compose-project en ontdekt het ene gedeelde interne netwerk uit de werkelijke Docker-netwerkverbindingen. Compose zet de projectnaam vóór dat netwerk, dus configureer of hardcode geen veronderstelde netwerknaam. Alleen de container die het archief maakt sluit aan op dat interne netwerk en kan de gefilterde proxy bereiken; deze krijgt geen onbewerkte socket. Onafhankelijke archiefverificatie blijft --network none gebruiken. Een ontbrekende proxy, onverwacht eindpunt, extern of dubbelzinnig gedeeld netwerk of een onbewerkte socketmount zorgt voor een fout voordat de toepassing stopt en vóór publicatie van een archief.
Test herstel naar een nieuw volume zonder het actieve volume te vervangen:
LIBRE_WEBUI_RESTORE_IMAGE="$image_ref" \
libre-webui-restore \
/var/backups/libre-webui/libre-webui-integrated-YYYYMMDDTHHMMSSZ.lwb \
libre-webui-restore-drill
De herstelhelper weigert een bestaand volume of configuratiedoel, verifieert archief en interne herstelinventaris in tijdelijke opslag, kopieert gegevens naar het nieuwe volume en schrijft herstelde runtime.json en secrets.json met privérechten. De actieve stack wordt nooit omgeleid of gestart. Controleer de herstelde configuratie, wijzig implementatiespecifieke waarden bewust en test het herstelde volume met een geïsoleerde stack.
Ollama-modellen kunnen opnieuw worden opgehaald. Docker Work-volumes, Kubernetes Work-PVC's en hostgebonden Work-mappen vallen buiten de toepassingsgegevensmap en vereisen eigen gecoördineerde momentopnamen en bewaarbeleid.
Updates
Libre WebUI heeft status, zelfs met een veranderlijke imagetag. Het basis-Compose-bestand markeert de toepassing permanent als uitgesloten van Watchtower. Upgrade alleen als gecoördineerde operatoractie:
- Leg de actieve image-ID vast en bepaal de beoordeelde vervanging als onveranderlijke digest.
- Voer
libre-webui recovery-checkuit, start de back-upservice en vereis een nieuw archief en verificatierapport voordat je verdergaat. - Stel
LIBRE_WEBUI_IMAGEin op de beoordeelde digest, haal deze op en maak alleenlibre-webuiopnieuw met Docker Compose. Verwijder of maak het gegevensvolume niet opnieuw. - Vereis geslaagde controles voor
/health/ready, aanmelden, sessie/geschiedenis, documentophaling en Work. Keer bij fouten terug naar de vastgelegde imagedigest; bewaar zowel de mislukte status als de geverifieerde back-up voor diagnose.
De hostsequens is bewust handmatig. Vervang de digest pas na beoordeling en controleer vóór ophalen het nieuwste paar .lwb en .json:
docker inspect libre-webui --format '{{.Config.Image}} {{.Image}}'
docker exec libre-webui \
libre-webui recovery-check --json --data-dir /app/backend/data
systemctl start libre-webui-backup.service
systemctl --no-pager --full status libre-webui-backup.service
ls -lt /var/backups/libre-webui/libre-webui-integrated-* | head
# Set LIBRE_WEBUI_IMAGE=ghcr.io/libre-webui/libre-webui@sha256:REVIEWED_DIGEST
# in the root-owned .env, then recreate only the application.
docker compose pull libre-webui
docker compose up -d --no-deps libre-webui
docker inspect libre-webui --format '{{.State.Health.Status}} {{.Image}}'
De optionele Watchtower-overschrijving met socket blijft alleen beschikbaar voor sidecars die expliciet in het basisbestand zijn gelabeld:
docker compose \
-f docker-compose.yml \
-f docker-compose.watchtower.yml \
up -d
Watchtower controleert Ollama en SearXNG elke 30 minuten. Ollama-modelgegevens blijven in het benoemde volume en SearXNG-configuratie in de bind mount. Het werkt Libre WebUI, cloudflared, de Work-socketproxy of Work-sandboxen niet bij. Een clientimplementatie volgt main; een experimentele instantie mag :dev selecteren, maar de toepassing vereist nog steeds dezelfde handmatige, door back-up geborgde upgrade. Koppel deze privé-solostack nooit aan persistentieservices van een team; implementeer in plaats daarvan de volledige teamtopologie.