De rondgang doorliep alle vijf de stappen goed en de CI-stap viel er daarna
alsnog op om, zonder melding: tussen "Rondgang geslaagd" en het einde zaten
tweeenhalve seconde en verder niets. Dat is het opruimblok.
- opruimen kan de uitslag niet meer bepalen; mislukt het, dan staat dat er
- het script eindigt expliciet met exitcode 0
- de CI drukt de exitcode van de stap af, want zonder dat getal was niet te
achterhalen waar een rode job vandaan kwam terwijl het scherm groen was
De postgres-race uit v0.8.06 is bevestigd opgelost: de rondgang liep in een
keer door.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
- tools/backup_rondgang.py draait backup en terugzetten tegen een echte
Docker-daemon in de CI: postgres-sidecar vullen, backuppen, alles weggooien,
terugzetten, en controleren dat de database daarna nog kan schrijven. Alle
bestaande backuptests vervangen Docker door een nep, dus die keten was nooit
bewezen
- daardoor gevonden: het terugzetten gaf de appdata de verkeerde eigenaar. Het
archief bevat de echte uid (postgres draait als 70 en zegt dat nergens in de
metadata); die gaat nu voor op PUID en op onze eigen uid
- een mislukte --update rolt terug naar de commit van ervoor en start die
opnieuw, in plaats van een stilstaande server en een rijtje commando's
- met een registry-image (SU_IMAGE) wordt er gepulld in plaats van gebouwd
- nieuw --reconfigure: bind, poort, account of hoofdmap wijzigen zonder de
broncode aan te raken
- nieuw --uninstall --purge: ook het volume, de gegevensmap en het account,
per onderdeel gevraagd. Zonder --purge somt --uninstall nu op wat blijft
- --dir wordt op een volledig pad gecontroleerd, het tijdelijke bestand voor
het beheerdersaccount is niet meer voorspelbaar, en er gaat nog een
rondgang naar de docker-daemon in plaats van twee
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
Ik ging ervan uit dat de bereikbaarheidscontrole niet kon slagen omdat de
runner in een eigen container zit. De eerste run liet zien dat hij wél slaagt:
"Antwoordt op http://127.0.0.1:5000". De exitcode wordt nu meegenomen, dus een
echte fout in de deploy laat de CI vallen in plaats van alleen in het log te
belanden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
- bijwerken vanuit de interface kon nooit werken: selfupdate schreef SU_TAG naar
<werkmap>/.env, maar die map is nergens in de container gemount. De
helper-container mount hem wel en zet de tag nu
- een vastgepinde versie kon niet bijgewerkt worden: `git reset --hard
origin/<tag>` bestaat niet. Resetten gaat nu naar FETCH_HEAD, wat voor takken
en tags allebei klopt
- de token stond in .git/config en in ps; hij gaat nu via een bestand met modus
0600 dat na afloop weer weg is (git via een credential-helper, curl via
--config). Schema en host komen uit bron_url, want git zoekt op exact die
combinatie
- nieuwe optie --base-dir PAD: de hoofdmap bij de installatie zetten in plaats
van achteraf in .env, waarna je de data alsnog moet verhuizen. Relatieve
paden en systeemmappen worden geweigerd
- nieuwe actie --doctor: een installatie doorlichten zonder iets te wijzigen —
container, bereikbaarheid op het ingestelde adres, het account versus SU_UID
in .env, of BASE_DIR echt gekoppeld is, de rechten van .env en de vrije
ruimte. Exitcode 1 bij fouten
- de CI draait --doctor na elke deploy tegen de echte installatie
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
- bij de installatie wordt gevraagd onder welk account Server Up draait: een
nieuw systeemaccount 'serverup', het account waarmee je werkt, een bestaand
account, of root zoals voorheen (--user NAAM slaat de vraag over)
- nieuw docker-entrypoint.sh: start als root, zet /data, stacks en backups
klaar, en zakt met setpriv af naar SU_UID:SU_GID met de capabilities die
nodig blijven (chown, dac_override, fowner - alle drie al in Docker's
standaardset)
- appdata blijft daarbij bewust ongemoeid; die mappen zijn van de apps zelf
- de gid van de docker-socket wordt uit de socket zelf gelezen, zodat het
account niet in de docker-groep hoeft (dat zou root op de host geven)
- installatieformulier vult PUID/PGID met de ids waaronder Server Up draait in
plaats van de 1000 die 44 sjablonen blind noemen
- een onleesbaar bestand laat de rest van de backup niet meer sneuvelen; wat
ontbreekt komt in het log en in de metadata (skipped)
- terugzetten herstelt het eigenaarschap van appdata, dat tarfile met
filter="data" laat vallen
- CI controleert in een echte container dat setpriv bestaat, dat er wordt
afgezakt, dat de socket-gid meekomt, dat /data schrijfbaar is en dat SU_UID=0
root laat blijven
- SU_UID=0 in .env is de ontsnappingsklep naar het oude gedrag
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
- apps/prometheus was kapot: het koppelde een lege map op /etc/prometheus en
startte met --config.file naar een bestand dat er nooit kwam. Er zit nu een
startconfiguratie bij, met koppelvelden naar node-exporter en cAdvisor
- De meeste apps van deze ronde zijn de ontbrekende helft van wat er al stond:
node-exporter, cAdvisor en Alertmanager maken Prometheus bruikbaar; Loki en
Alloy bewaren logs waar Dozzle alleen live meekijkt; Unbound zoekt DNS zelf op
in plaats van door te vragen aan Google of Cloudflare
- Domeinen die ontbraken: evcc (energie), Healthchecks (cronbewaking),
Infisical (geheimen voor applicaties), Music Assistant, pgAdmin
- Verder Crafty, Manyfold, ErsatzTV, FitTrackee, ComfyUI, Omada, en de zwaardere
Zabbix, Graylog, Wazuh, NetBox, Seafile en OpenCloud. Van 218 naar 241 apps
- Nieuw tools/controleer_images.py: controleert of elk image echt bestaat. De
ad-hoc versie draaide met zestien verzoeken tegelijk en meldde negen
ontbrekende images die alle negen wel bestonden -- registries knijpen af, en
dat is niet te onderscheiden van een ontbrekend image. Nu vier tegelijk met
opnieuw proberen, en als stap in de drie workflows
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
De testsuite controleert alleen of een sjabloon geldige YAML oplevert. Een
depends_on in de verkeerde vorm of network_mode naast networks is prima YAML en
een kapotte stack — dat merkte je pas als iemand de app installeerde.
- tools/render_voor_validatie.py rendert elk sjabloon naar echte
compose-bestanden; sjablonen met schakelbare onderdelen leveren meerdere
varianten op (standaard, alles aan, met en zonder VPN), zodat ook de
conditionele blokken langskomen die met alleen de standaardwaarden nooit
gerenderd worden
- Nieuwe stap in build.yml, deploy.yml en deploy-prod.yml die er
`docker compose config` overheen haalt. Renderen gebeurt in de
python-container, valideren op de runner zelf: Python en Docker staan in CI
niet op dezelfde plek
145 bestanden uit 142 sjablonen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
De stap "Instellingen en versie klaarzetten" faalde op `touch: cannot touch
'.env': Permission denied`. Het bestand was met sudo aangemaakt en dus van
root, terwijl de runner als een gewone gebruiker draait. Daarmee brak de hele
deploy af op iets dat geen blokkade hoort te zijn.
- SU_TAG wordt nu altijd via de omgeving aan `docker compose` meegegeven
(export SU_TAG). Het wegschrijven in .env blijft de voorkeur, omdat het een
handmatige `docker compose up` overleeft, maar het is niet meer nodig om te
kunnen deployen.
- Is .env niet schrijfbaar, dan meldt de log wie de eigenaar is en welk
chown-commando dat rechtzet, en loopt de deploy gewoon door.
- Het aanmaken vanuit .env.example faalt niet meer hard.
Positief neveneffect van de vorige fix: de log toont nu "Werkmap:
/opt/docker/server-up", dus de cd landt op de juiste plek.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
.env verschijnt niet in de deploy-map en BIND moet nog steeds in
docker-compose.yml worden aangepast. De stappen die .env aanmaken, VERSION
schrijven en compose draaien vertrouwden alle drie op 'working-directory:'.
Wordt dat door de runner niet toegepast, dan belanden die bestanden in de
werkmap van de runner en draait de container vanaf de compose daar - terwijl
de rsync (die een expliciet pad gebruikt) wel netjes naar /opt/docker/server-up
schrijft. Dat verklaart precies wat er gemeld wordt.
- Alle stappen doen nu 'cd "$DEPLOY_DIR"' in het script zelf.
- De run-log toont de werkmap, de inhoud van .env, de compose-projectmap
volgens de draaiende container en de daadwerkelijke poortbinding. Daarmee is
in de log te zien waar alles landt in plaats van het te moeten raden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
BIND bleef niet staan. Oorzaak: docker-compose.yml staat in Git, dus rsync zet
dat bestand bij elke deploy terug; een wijziging daarin op de server houdt het
nooit. De juiste plek is .env ernaast, maar dat bestand bevatte na een deploy
alleen een kale SU_TAG-regel en was daarmee niet vindbaar.
- Bestaat er nog geen .env, dan zet de deploy hem op vanuit .env.example, met
alle instellingen erin en uitleg per sleutel. Een bestaande .env blijft
ongemoeid (rsync slaat hem al over sinds 6d5453e).
- De deploy logt nu welke instellingen actief zijn, zodat je in de run-log ziet
wat er staat.
- Waarschuwing in docker-compose.yml zelf dat je BIND daar niet moet aanpassen
maar in .env. Dat bestand wordt toch elke deploy teruggezet, dus de tip staat
er altijd.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
.env staat in .gitignore, dus de checkout bevat er geen. De rsync-stap draait
met --delete, waardoor /opt/docker/server-up/.env bij elke deploy verdween --
inclusief BIND, PORT en SU_IMAGE die de beheerder daar had gezet.
SU_TAG werd daarna wel opnieuw geschreven, dus dat viel niet op, maar een
BIND=0.0.0.0 raakte je elke run kwijt en daarmee de bereikbaarheid van de
interface van buiten de server.
--exclude='.env' toegevoegd in deploy.yml en deploy-prod.yml.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
De testsstap die ik in v0.5.00 toevoegde blokkeerde de deploy: de runner heeft
geen python3-venv, dus `python3 -m venv` faalde en alle volgende stappen werden
overgeslagen.
- Tests draaien nu in python:3.12-slim via docker, dat de runner sowieso heeft.
Bijkomend voordeel: dezelfde Python-versie als waarop de app in productie
draait, in plaats van wat er toevallig op de runner staat.
- PYTHONDONTWRITEBYTECODE en -p no:cacheprovider voorkomen dat de container
root-eigen __pycache__/.pytest_cache in de werkmap achterlaat, wat de
rsync-stap en volgende checkouts zou kunnen hinderen.
- Pip-cache in een named volume, zodat een herhaalde run niet opnieuw downloadt.
Toegepast in deploy.yml, deploy-prod.yml en build.yml.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
- Update-kanalen stable/beta in plaats van het vinkje "pre-releases meenemen".
Stable ziet alleen releases, beta ook pre-releases; een release telt hoger
dan zijn eigen beta (0.5.10-beta1 < 0.5.10). UPDATE_INCLUDE_PRERELEASE
migreert automatisch naar het beta-kanaal.
- Nieuw core/selfupdate.py: image uit de registry ophalen, SU_TAG wegschrijven
en de eigen container laten hercreeren door een korte helper-container die op
het nieuwe image draait (bevat de docker- en compose-CLI al). Een container
kan zichzelf niet hercreeren, vandaar de helper.
- Deploy-map wordt uitgelezen uit de compose-labels van de eigen container.
Ontbreken die, draait de container niet vanaf een registry-image of is de
docker-socket er niet, dan meldt de UI waarom bijwerken niet kan.
- Vorige tag wordt onthouden; terugrolknop in de instellingen.
- Update-check een uur gecached, met geforceerde check via de knop; eerder deed
elke paginalading een netwerkverzoek. Laatst-gecontroleerd zichtbaar.
- Registry-inloggegevens instelbaar; het token komt net als de git-tokens nooit
terug via de API en leeg laten betekent ongewijzigd.
- docker-compose.yml gebruikt ${SU_IMAGE:-server-up}:${SU_TAG:-latest}; zonder
SU_IMAGE blijft lokaal bouwen werken zoals voorheen.
- deploy-prod.yml bouwt en pusht het image in dezelfde job wanneer SU_IMAGE
ingesteld is; build.yml is nu alleen handmatig. Bewust geen aparte
build-workflow op dezelfde tag: bij een runner met een job tegelijk zou de
deploy wachten op een build die zelf nog in de wachtrij staat.
- docs/updates.md: werking, complete Forgejo-instelling (registry, tokens,
variables, secrets, runners), release-procedure voor stable en beta,
terugrollen en probleemoplossing.
- Tests uitgebreid naar 148 (kanaallogica, cache, image-afleiding incl.
registry-poortnummers, tag-validatie, .env-schrijven, tokenlek).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
Server Up beheerde de Docker-daemon als root zonder enige vorm van
authenticatie: elke /api/*-route was gelijk aan root-toegang op de host.
- Authenticatie toegevoegd (core/auth.py): lokale accounts met scrypt-hash,
sessiecookie (HttpOnly, SameSite=Strict) en optionele trusted-proxy-header
SSO die alleen vanaf geconfigureerde proxy-IP's wordt vertrouwd.
- before_request-guard schermt alle API-routes af; loginscherm en
eerste-account-setup in de UI.
- CSRF-token verplicht op elke mutatie; GET-varianten van state-wijzigende
routes verwijderd (o.a. /api/docker/restart was via <img> te triggeren).
- Path traversal in /api/store/install gedicht; gedeelde safe_name()-validatie
voor stack-, instantie- en repo-namen.
- Git-tokens worden niet meer teruggegeven via /api/repos en /api/settings
(has_token-vlag); settings-PUT wist een bestaand token niet meer en kan AUTH
niet overschrijven.
- Git-URL's beperkt tot http(s)/ssh/scp-syntax; ext::-transport (voert een
shell-commando uit) en file:// worden geweigerd.
- Boilerplate-templates renderen in een SandboxedEnvironment (SSTI).
- Automatisch syncen van repo's bij boot standaard uit (AUTO_SYNC_ON_BOOT),
optionele commit-pinning per repo.
- Waitress in plaats van de Flask-ontwikkelserver, MAX_CONTENT_LENGTH,
ProxyFix, en CSP/X-Frame-Options/nosniff/Referrer-Policy headers.
- Front-end libraries (Tailwind, Alpine, htmx) lokaal meegeleverd i.p.v. CDN;
Google Fonts verwijderd. Werkt nu ook offline.
- SSH host-key-verificatie aan (accept-new + /data/known_hosts).
- Lichte /healthz voor de healthcheck i.p.v. `docker info`.
- config.json en secret.key met 0600-rechten.
- Poort standaard op 127.0.0.1 gebonden.
- Audit-log gebruikt één gedeelde SQLite-verbinding (fd-lek per job verholpen);
joblogs afgekapt op 2000 regels.
- VERSION-bestand is de enige bron voor het versienummer.
- pytest-suite toegevoegd (74 tests) en als stap in beide deploy-workflows.
- fix-config.sh verwijderd (hardgecodeerd intern IP, overschreef config).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb