All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 6m45s
Sjablonen komen uit de git-cache van een app-repo, niet uit het image. Welke branch die volgt was nergens te zien en alleen te wijzigen door de repo te verwijderen en opnieuw toe te voegen — waarbij de id een volgnummer krijgt en het bronsjabloon van bestaande apps onvindbaar wordt. - PUT /api/repos/<id> past naam, branch, submap en optioneel de URL aan; de id blijft staan zodat .serverup.json van bestaande stacks blijft kloppen. - Een leeg tokenveld laat het bestaande token staan. - De repolijst toont de branch en heeft een bewerkknop; na opslaan wordt de repo meteen opnieuw opgehaald. - Nieuwe auditactie 'edit' vertaald in nl en en. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H9Vcwu7owJZ3nj5dCf4VFT
2476 lines
124 KiB
Markdown
2476 lines
124 KiB
Markdown
# v0.8.23-beta — De branch van een repo is te zien en te wijzigen
|
||
|
||
Sjablonen komen uit de git-cache van een app-repo, niet uit het image van
|
||
Server Up. Welke branch die repo volgt was nergens te zien, en alleen te
|
||
veranderen door de repo te verwijderen en opnieuw toe te voegen. De id wordt
|
||
dan uit de URL afgeleid en krijgt er een volgnummer bij zodra hij al bestaat —
|
||
en juist die id staat in de metadata van elke app uit die repo. Overstappen
|
||
maakte zo stilzwijgend het bronsjabloon van je apps onvindbaar.
|
||
|
||
- **De branch staat in de lijst.** Zo is te zien waarom een app een oudere
|
||
versie van zijn sjabloon blijft renderen.
|
||
- **Bewerken zonder de id te raken.** Naam, branch en submap zijn aan te passen;
|
||
na opslaan wordt de repo meteen opnieuw opgehaald.
|
||
- **Een leeg tokenveld wist het token niet.** De interface krijgt het token
|
||
nooit te zien, dus zou opslaan hem anders weggooien.
|
||
|
||
# v0.8.22-beta — UniFi leest zijn databasegegevens opnieuw in
|
||
|
||
Het linuxserver-image schrijft `/config/data/system.properties` alleen bij de
|
||
allereerste start ("Only evaluated on first run"). Alles wat daarna in Compose
|
||
verandert — de toegevoegde `authSource=admin`, een ander wachtwoord — kwam daar
|
||
dus nooit in terecht. UniFi bleef verbinden met de gegevens van de allereerste
|
||
installatie, en MongoDB antwoordde met `UserNotFound: unifi@unifi`.
|
||
|
||
- **De databasegegevens worden bij elke start gelijkgetrokken.** De
|
||
reparatiecontainer zet `db.mongo.uri`, `statdb.mongo.uri`, `db.mongo.local`
|
||
en `unifi.db.name` opnieuw uit de actuele Compose-omgeving. Andere
|
||
instellingen in het bestand blijven staan.
|
||
- **Bijzondere tekens gaan ge-url-codeerd de URI in.** Een `@` of `/` in het
|
||
wachtwoord brak de verbindingsreeks anders doormidden.
|
||
- **Het bestand houdt zijn eigenaar en rechten.** Het wordt op zijn plaats
|
||
overschreven, zodat UniFi er zelf in kan blijven schrijven.
|
||
- **Het oude wachtwoord telt mee bij de reparatie.** Wijkt `.env` af van
|
||
waarmee de database ooit is aangemaakt, dan meldt de reparatie zich aan met
|
||
het wachtwoord uit `system.properties` en trekt daarna alles gelijk — in
|
||
plaats van je met een dode stack achter te laten.
|
||
|
||
# v0.8.21-beta — Wijzigingen blijven staan, UniFi komt online
|
||
|
||
Een geslaagde instellingenwijziging werd meteen weer teruggedraaid: het
|
||
audit-logboek las het IP-adres van de aanvrager pas uit in de achtergrondthread,
|
||
waar geen request meer bestaat. De `RuntimeError` die daarop volgde kwam in de
|
||
foutafhandeling terecht, die netjes de backup terugzette — inclusief het oude
|
||
compose-bestand. De MongoDB-reparatie van v0.8.20 kon zo nooit landen.
|
||
|
||
- **Het adres wordt vóór de job vastgelegd.** Herconfigureren, koppelen, een
|
||
backup maken en terugrollen loggen weer wie de actie startte, zonder crash.
|
||
- **UniFi repareert ook een oud database-volume.** Het init-script probeert
|
||
achtereenvolgens de beheerders `unifi` en `root` en herkent een server zonder
|
||
authenticatie, in plaats van te veronderstellen dat `root` bestaat.
|
||
- **De healthcheck van Mongo gebruikt geen inloggegevens meer.** Op een volume
|
||
zonder de gebruiker `root` bleef de container ongezond, waardoor de
|
||
reparatiecontainer nooit startte.
|
||
- **Bestaande rollen blijven staan.** De reparatie trekt alleen het wachtwoord
|
||
gelijk en vult ontbrekende rollen aan — inclusief `backup` en `restore`, die
|
||
mongodump en mongorestore nodig hebben.
|
||
- **Backups van MongoDB werken.** Het dumpcommando gebruikt de beheerder uit
|
||
het compose-bestand in plaats van de verzonnen gebruiker `app`.
|
||
- **Elke app met een webinterface krijgt een openknop op de kaart.** Het adres
|
||
stond alleen in de modal met inloggegevens, waar je niet kijkt als je de app
|
||
gewoon wilt openen.
|
||
- **Geen dubbele regels meer in het joblog.** Bij een trage stap vertrokken
|
||
meerdere polls met dezelfde offset, waardoor hetzelfde blok tot vijf keer in
|
||
beeld kwam.
|
||
|
||
# v0.8.20-beta — UniFi herstelt zijn MongoDB-gebruiker
|
||
|
||
Een eerder geïnitialiseerd Mongo-volume voerde het init-script niet opnieuw uit.
|
||
Daardoor bleef UniFi herstarten met `UserNotFound: unifi@unifi` en kwam de
|
||
webinterface nooit online.
|
||
|
||
- **Idempotente databasereparatie.** Een korte init-container maakt de
|
||
UniFi-gebruiker aan of werkt hem bij, ook bij een bestaand Mongo-volume.
|
||
- **Oude installaties worden ondersteund.** Zowel de officiële authsource
|
||
`admin` als de eerder opgeslagen authsource `unifi` wordt gerepareerd.
|
||
- **UniFi wacht op de reparatie.** De applicatie start pas wanneer Mongo gezond
|
||
is en de gebruikerscontrole succesvol is afgerond.
|
||
- **Geen misleidende poort 8843.** De appkaart toont alleen werkelijk op de host
|
||
gepubliceerde poorten; UniFi's ongepubliceerde gastportaalpoort verdwijnt.
|
||
|
||
# v0.8.19-beta — Mobiele appacties reageren weer
|
||
|
||
In het mobiele actiemenu kregen normale acties zonder expliciete
|
||
uitschakelstatus toch het HTML-attribuut `disabled`. Daardoor deed onder meer
|
||
de knop Verwijderen zichtbaar helemaal niets.
|
||
|
||
- **Verwijderen is weer klikbaar.** Alleen acties die expliciet als
|
||
uitgeschakeld zijn gemarkeerd krijgen nog het `disabled`-attribuut.
|
||
- **De actie loopt vóór het menu sluit.** Zo blijft de Alpine-scope beschikbaar
|
||
totdat de verwijdermodal daadwerkelijk is geopend.
|
||
- **Echte DOM-regressietest.** De test rendert de appkaart met Alpine en klikt
|
||
zowel de desktop- als mobiele verwijderroute volledig door.
|
||
|
||
# v0.8.18-beta — UniFi opent via HTTPS
|
||
|
||
De UniFi-container was wel correct op zijn HTTPS-poort gepubliceerd, maar
|
||
Server Up maakte er altijd een `http://`-link van. Daardoor leek de app
|
||
onbereikbaar terwijl de browser met het verkeerde protocol verbinding maakte.
|
||
|
||
- **UniFi declareert HTTPS expliciet.** Het template vermeldt voortaan zowel
|
||
het protocol als de interne webpoort 8443.
|
||
- **De juiste hostpoort wordt gekozen.** Server Up koppelt de interne webpoort
|
||
aan de ingestelde hostpoort, ook wanneer een databasepoort eerder in het
|
||
Compose-bestand staat.
|
||
- **Bestaande installaties werken mee.** Ontbrekende installatiemetadata wordt
|
||
uit het actuele bronsjabloon aangevuld; opnieuw installeren is niet nodig.
|
||
- **Eigen IP-adressen blijven correct.** Een UniFi-stack met een eigen IP opent
|
||
als `https://<app-ip>:8443`.
|
||
|
||
# v0.8.17-beta — Elf nieuwe apps voor beheer, automatisering en monitoring
|
||
|
||
De app-store bevatte nog weinig lichte infrastructuurapps die zowel op een
|
||
reguliere x86-server als op een 64-bits Raspberry Pi werken. Daarom zijn elf
|
||
nieuwe templates toegevoegd met expliciete AMD64- en ARM64-ondersteuning.
|
||
|
||
- **Remote beheer:** RustDesk Server, MeshCentral en Apache Guacamole. Guacamole
|
||
genereert bij de eerste start automatisch het officiële PostgreSQL-schema.
|
||
- **Identiteit en discovery:** LLDAP en Consul bieden respectievelijk een lichte
|
||
LDAP-directory en service discovery met DNS en healthchecks.
|
||
- **Monitoring:** VictoriaMetrics en OpenObserve voegen efficiënte opslag toe
|
||
voor metrics en voor logs, metrics en traces.
|
||
- **Automatisering:** Activepieces en Windmill bieden visuele workflows,
|
||
scripts en interne apps. De Windmill-worker krijgt standaard geen toegang tot
|
||
de Docker-socket.
|
||
- **Bestandsoverdracht:** PairDrop maakt lokale bestandsoverdracht vanuit de
|
||
browser mogelijk.
|
||
- **Nomad als veilig control-plane:** de Nomad-container draait alleen als
|
||
server met webinterface. Workload-clients horen rechtstreeks op de hosts te
|
||
worden geïnstalleerd, omdat HashiCorp Nomad-clients in Docker niet officieel
|
||
ondersteunt.
|
||
- **Cataloguscontrole:** gerichte tests bewaken dat alle elf apps beschikbaar
|
||
blijven voor AMD64 en ARM64, dat Nomad geen containerized client wordt en dat
|
||
Guacamole zijn database vóór de webapp initialiseert.
|
||
|
||
# v0.8.16-beta — De startwizard blijft gesloten
|
||
|
||
**De knop Sluiten verborg de optionele startwizard alleen tijdelijk.** De
|
||
server kreeg daardoor nooit door dat de gebruiker de wizard had overgeslagen en
|
||
opende hem bij iedere nieuwe paginalading opnieuw.
|
||
|
||
- **Sluiten wordt blijvend onthouden.** De knop markeert de wizard nu via de API
|
||
als afgerond en sluit pas nadat de server dit heeft opgeslagen.
|
||
- **Een ontbrekende appmap reset de wizard niet meer.** Een nog niet gemounte of
|
||
tijdelijk onbereikbare library wordt als padprobleem behandeld en overschrijft
|
||
niet langer een expliciete keuze van de gebruiker.
|
||
- **Bewust opnieuw starten blijft mogelijk.** De knop “Opnieuw uitvoeren” in
|
||
Instellingen wist de voltooiingsstatus en opent de wizard weer vanaf het
|
||
begin.
|
||
|
||
# v0.8.15-beta — Apps verwijderen en mobiele schermen werken weer
|
||
|
||
**De verwijderknop stopte containers en verwijderde volumes, maar liet de
|
||
appconfiguratie staan.** Daardoor bleef de app in Server Up zichtbaar en leek de
|
||
knop niets te doen. Daarnaast konden modals en instellingen op een klein scherm
|
||
buiten de viewport vallen zonder dat de inhoud bereikbaar was.
|
||
|
||
- **Verwijderen doet nu wat de keuze zegt.** De veilige keuze verwijdert de
|
||
appconfiguratie, maar bewaart appdata en named volumes. De expliciete keuze
|
||
“app en alle data verwijderen” wist ook appdata en niet-externe volumes.
|
||
- **Overgenomen apps zijn verwijderbaar.** Bij een app die als symlink is
|
||
gekoppeld wordt alleen de Server Up-verwijzing weggehaald; de externe
|
||
Compose-map van de gebruiker blijft bestaan.
|
||
- **Duidelijke voortgang en fouten.** De dialoog onthoudt de juiste app,
|
||
voorkomt dubbele opdrachten en toont zowel succes als backendfouten.
|
||
- **Modals passen op ieder mobiel scherm.** Dialogen en de setupwizard gebruiken
|
||
de dynamische viewporthoogte, houden kop en knoppen in beeld en maken de
|
||
inhoud zelfstandig scrollbaar.
|
||
- **Instellingen blijven binnen de schermbreedte.** Brede keuzerijen kunnen
|
||
horizontaal scrollen, kaartkoppen lopen om en invoervelden mogen binnen hun
|
||
grid krimpen. Ook het loginvenster blijft op een laag scherm bereikbaar.
|
||
|
||
# v0.8.14-beta — Raspberry Pi krijgt werkende en eerlijke apps
|
||
|
||
**De UniFi-template startte een MongoDB-container, maar maakte de database-
|
||
gebruiker en rollen die UniFi nodig heeft niet aan.** Daardoor bleef de
|
||
controller zonder werkende database hangen. Tegelijk kon de app-store op een
|
||
Raspberry Pi ook images aanbieden die uitsluitend voor AMD64 bestaan; dat werd
|
||
pas zichtbaar nadat Docker de pull weigerde.
|
||
|
||
- **UniFi initialiseert MongoDB volledig.** Een init-script maakt de UniFi-
|
||
gebruiker met de voorgeschreven rollen aan, beide containers gebruiken
|
||
`authSource=admin` en UniFi wacht op de database-healthcheck.
|
||
- **Geschikt voor een 64-bits Raspberry Pi 4.** MongoDB staat vast op
|
||
`4.4.29`: deze tag heeft een ARM64-image en vermijdt de ARMv8.2-eis van
|
||
MongoDB 5 en hoger. De hoofdimage ondersteunt AMD64 en ARM64.
|
||
- **Platformcontrole vóór installatie.** Templates kunnen hun ondersteunde
|
||
Docker-architecturen vastleggen. De catalogus legt een mismatch uit, schakelt
|
||
de knop uit en de API weigert dezelfde installatie ook rechtstreeks.
|
||
- **Elf AMD64-only apps zijn gemarkeerd.** Alle 318 gebruikte image-manifests
|
||
zijn gecontroleerd. Cal.com, ComfyUI, Dawarich, Focalboard, Formbricks,
|
||
Mattermost, Pinchflat, Pixelfed, TubeArchivist, Unbound en Wazuh bevatten
|
||
momenteel minstens één image zonder ARM64-variant en worden daarom niet meer
|
||
als installeerbaar op een Pi getoond.
|
||
- **Init-scripts houden hun uitvoerrecht.** Tekstbestanden die tijdens het
|
||
renderen worden geschreven nemen voortaan de bestandsmodus van het sjabloon
|
||
over.
|
||
|
||
Een Mongo-init-script draait alleen bij een lege database. Server Up verwijdert
|
||
bestaande data daarom niet automatisch. Verwijder bij een eerdere mislukte
|
||
UniFi-installatie eerst bewust het oude Mongo-volume, of installeer onder een
|
||
nieuwe instantienaam.
|
||
|
||
# v0.8.13-beta — De update haalt nu de juiste release op
|
||
|
||
**Bijwerken vanuit de interface vroeg Docker om een image dat niet bestond.**
|
||
Forgejo noemt een release `v0.8.13`, terwijl de workflows bewust image
|
||
`:0.8.13` publiceren. De interface stuurde de Git-tag mét `v` door en dus
|
||
mislukte iedere beta- en stable/main-update bij `docker pull`.
|
||
|
||
- **Eén expliciete registry-tag.** De updatecheck geeft naast de Git-tag nu
|
||
`image_tag` terug. Frontend, endpoint en pull-laag normaliseren allemaal de
|
||
ene voorloop-`v`. De workflows publiceren ook `:v<versie>` als tijdelijke
|
||
compatibiliteitstag, zodat de al geïnstalleerde defecte updater zichzelf naar
|
||
deze reparatie kan brengen.
|
||
- **De herstart gebruikt exact het bestaande Compose-project.** Projectnaam,
|
||
projectmap en alle configuratiebestanden komen uit de containerlabels.
|
||
`--no-build --pull never` voorkomt dat Compose tijdens de update oude
|
||
broncode bouwt of onverwacht een andere tag ophaalt.
|
||
- **Tag en versie blijven bij elkaar.** De helper en beide deploy-workflows
|
||
schrijven `SU_TAG` en `SU_VERSION` samen. Ook na een handmatige Compose-
|
||
herstart toont en draait Server Up daardoor dezelfde beta- of releaseversie.
|
||
- **Terugrollen blijft naar een echte versie wijzen.** Als de container via de
|
||
beweeglijke alias `beta` of `latest` draaide, wordt voortaan het ingebakken
|
||
OCI-versielabel als terugrolpunt onthouden.
|
||
|
||
# v0.8.12-beta — De image-controle van twaalf minuten naar seconden
|
||
|
||
De controle of elk image nog bestaat kostte elf à twaalf minuten, bij élke push,
|
||
ook als er geen enkel sjabloon was gewijzigd. Gemeten: 709 seconden, terwijl de
|
||
netwerktijd voor 318 images maar zo'n 48 seconden is.
|
||
|
||
Twee dingen zaten fout.
|
||
|
||
**Het aantal tegelijk gold in totaal in plaats van per registry.** Registries
|
||
knijpen per host af, en tweederde van de catalogus staat op Docker Hub. Met vier
|
||
tegelijk in totaal stonden ghcr, lscr en quay dus achter Docker Hub in de rij te
|
||
wachten zonder dat dat iets oploste. Nu geldt de limiet van vier per registry en
|
||
lopen registries naast elkaar.
|
||
|
||
**Elke ronde controleerde alles opnieuw.** Geslaagde controles blijven nu een
|
||
week staan, met een vaste spreiding per image zodat niet de hele catalogus op
|
||
dezelfde dag verloopt. De sleutel is de volledige naam mét tag: een sjabloon dat
|
||
naar een andere tag gaat wijzen levert een nieuwe sleutel op en wordt dus altijd
|
||
gecontroleerd. Mislukkingen komen er nooit in — een ontbrekend image blijft elke
|
||
ronde gemeld tot het is opgelost, want daar is deze controle voor.
|
||
|
||
Gemeten na de wijziging:
|
||
|
||
```
|
||
koude cache 318 gecontroleerd 43 s
|
||
warme cache 0 gecontroleerd 0 s
|
||
```
|
||
|
||
Met `--alles` negeer je de cache en wordt alles opnieuw nagelopen.
|
||
|
||
# v0.8.11-beta — Overnemen en verwijderen
|
||
|
||
Handmatig gestarte apps stonden in de lijst, maar je kon er alleen mee starten,
|
||
stoppen en logs bekijken. Nu ook:
|
||
|
||
**Overnemen.** Draait er een compose-app die je buiten Server Up om hebt
|
||
gestart, dan trek je hem er met één knop bij. Er wordt **niets verplaatst of
|
||
gekopieerd**: de stackmap wordt een verwijzing naar de map die je al hebt.
|
||
Kopiëren zou de relatieve paden in je compose-bestand breken — die worden
|
||
opgezocht vanaf de map waar dat bestand staat, en dat zou dan een andere zijn.
|
||
|
||
Kan Server Up niet bij die map — hij ziet alleen wat via `BASE_DIR` gekoppeld
|
||
is — dan zegt hij dat, met waar je hem neer moet zetten. Overnemen zonder erbij
|
||
te kunnen heeft geen zin.
|
||
|
||
**Verwijderen.** Voor een container die niet bij een app hoort. De vraag
|
||
vermeldt dat alleen de container weggaat, niet zijn gegevens of zijn image.
|
||
|
||
Hoort hij bij een compose-project, dan staat er bovendien bij dat hij **terugkomt
|
||
zodra dat project ergens anders start**, en dat overnemen waarschijnlijk is wat
|
||
je zoekt. Dat is de reden dat de overnemen-knop links van de verwijderknop
|
||
staat: verwijderen lost dat geval niet op.
|
||
|
||
Een overgenomen app verwijderen haalt alleen de verwijzing weg. Jouw map blijft
|
||
staan — `shutil.rmtree` zou daar trouwens sowieso op weigeren ("Cannot call
|
||
rmtree on a symbolic link") en de verwijdering halverwege laten stranden.
|
||
|
||
# v0.8.10-beta — Een repo verwijderen mag niet struikelen
|
||
|
||
`DELETE /api/repos/<id>` las `r["id"]` zonder controle. Staat er één regel
|
||
zonder id in je configuratie, dan gaf verwijderen een 500 — dezelfde fout die
|
||
`GET /api/repos` had en die de hele app store leeg liet. Nu wordt zo'n regel
|
||
overgeslagen.
|
||
|
||
Verwijderen haalt alle regels met die id weg, dus een dubbele vermelding is in
|
||
één klik opgeruimd.
|
||
|
||
# v0.8.09-beta — Twee repo's met dezelfde naam braken de app store
|
||
|
||
In een configuratie met twee repo's die dezelfde `id` dragen, tekende de app
|
||
store geen kaarten terwijl de knop "Nog N tonen" een dalend getal liet zien.
|
||
Klikken verlaagde het getal met zestig en er verscheen niets.
|
||
|
||
De interface tekent de repolijst met `:key="repo.id"`. Dubbele keys zijn voor
|
||
Alpine ongedefinieerd gedrag — je krijgt ontbrekende of verkeerde blokken. De
|
||
tellers rekenen ondertussen gewoon door over de gegevens, dus je ziet een getal
|
||
onder een leeg scherm en nergens een fout.
|
||
|
||
`GET /api/repos` geeft een id nu nog maar één keer terug. Dat is geen
|
||
schoonheidsreparatie: zolang er twee in zaten, was de hele storepagina stuk.
|
||
|
||
# v0.8.08-beta — Paden rechtzetten en er verandert niets
|
||
|
||
Zet je in Instellingen → Paden je mappen goed, dan bleef de appslijst leeg. Niet
|
||
omdat er iets mis was — de API leverde de apps gewoon — maar omdat de pagina ze
|
||
nooit opnieuw ophaalde. `goto()` wisselt alleen van pagina, en `saveSettings()`
|
||
verversde niets. Je keek dus naar de lijst zoals die bij het laden van de pagina
|
||
was: leeg. Een herlaadactie loste het op, als je dat wist.
|
||
|
||
Twee dingen daaraan:
|
||
|
||
- Verandert een pad, dan worden de appslijst en de containers opnieuw
|
||
opgehaald. Alleen bij een padwijziging, zodat een gewone instelling niet
|
||
telkens twee verzoeken kost.
|
||
- **Een geweigerde opslag meldde zich als "opgeslagen".** `saveSettings()` gooide
|
||
het antwoord van de server weg en zette onvoorwaardelijk het vinkje. Sinds
|
||
v0.8.05 een pad buiten de gekoppelde map weigert, is dat het verschil tussen
|
||
"aangepast" en "er is niets gebeurd" — en je zag alleen het vinkje. De
|
||
foutmelding van de server komt nu in beeld.
|
||
|
||
# v0.8.07-beta — "Rondgang geslaagd" en toch een rode job
|
||
|
||
De backup-rondgang doorliep alle vijf de stappen goed — dump, archief, alles
|
||
weggooien, terugzetten, eigenaar bewaard, database schrijft weer — en de
|
||
CI-stap viel er daarna alsnog op om. Zonder melding: tussen "Rondgang geslaagd"
|
||
en het einde van de stap zaten tweeënhalve seconde en verder niets.
|
||
|
||
Dat is het opruimblok. Dat hoort de uitslag niet te bepalen: of een
|
||
testcontainer zich laat verwijderen zegt niets over of backups werken. Een fout
|
||
daar liet nu de hele stap vallen, en omdat er niets werd afgedrukt was er ook
|
||
niets aan te zien.
|
||
|
||
- opruimen kan de uitslag niet meer bepalen; mislukt het, dan staat dát er
|
||
- het script eindigt expliciet met exitcode 0, zodat er geen twijfel over de
|
||
afloop kan bestaan
|
||
- de CI drukt de exitcode van de stap af. 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 hiermee bevestigd opgelost: de rondgang liep in
|
||
één keer door.
|
||
|
||
# v0.8.06-beta — De backuptest in de CI struikelde over postgres
|
||
|
||
Vier CI-runs op rij faalden, en niet op iets in Server Up: de backup-rondgang
|
||
die sinds v0.7.96 in de CI draait, viel om op
|
||
|
||
```
|
||
psql: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed
|
||
```
|
||
|
||
Het officiële postgres-image start bij een eerste run **twee** servers: eerst
|
||
een tijdelijke op de socket om de init-scripts te draaien, die daarna stopt, en
|
||
pas dan de echte. `pg_isready` meldt tijdens die tijdelijke server al "klaar".
|
||
Kwam de volgende opdracht net in het gat daartussen, dan was de socket weg. Een
|
||
race, dus soms goed en soms niet.
|
||
|
||
De wachtlus kijkt nu niet meer naar `pg_isready` maar voert echte query's uit,
|
||
en gaat pas door na twee geslaagde achter elkaar — één toevallige treffer op de
|
||
tijdelijke server overleeft dat niet. Bij een time-out staat de laatste fout
|
||
erbij in plaats van alleen "kwam niet op".
|
||
|
||
Verder wordt een achtergebleven testcontainer nu vóór de start opgeruimd. Een
|
||
geannuleerde run draait zijn opruimblok niet, en een half geïnitialiseerde
|
||
datamap levert daarna raadselachtige fouten op.
|
||
|
||
De deploy zelf werkte al die tijd gewoon; deze stap draait er ná en liet alleen
|
||
de job rood kleuren.
|
||
|
||
# v0.8.05-beta — Een onbereikbaar pad liet zich gewoon opslaan
|
||
|
||
Dit is de oorzaak van de lege appslijst waar de vorige vier uitgaven omheen
|
||
hebben gezocht.
|
||
|
||
`LIBRARY_DIR` stond op `/srv/serverup/stacks`, terwijl alleen `/opt/serverup` in
|
||
de container gekoppeld is. Dat pad bestaat binnen de container niet eens — dus
|
||
geen enkele app in de lijst, en alle containers onder "handmatig gestart", want
|
||
`beheerd` leest dezelfde map. Nergens stond waarom.
|
||
|
||
De controle daarop bestond al sinds v0.7.92: `paden.bereikbaar()` weet dat de
|
||
container alleen bij paden onder `BASE_DIR` kan, en geeft de exacte regel die je
|
||
in `.env` moet zetten. Hij hing alleen aan het veld **Hoofdmap** en aan het
|
||
**verhuizen**. De drie padvelden eronder schrijven rechtstreeks naar
|
||
`PUT /api/settings` — en daar stond geen wacht. Een pad buiten de koppeling liet
|
||
zich dus gewoon opslaan, waarna alles stilviel.
|
||
|
||
- `PUT /api/settings` weigert nu een `LIBRARY_DIR`, `DATA_DIR` of `BACKUP_DIR`
|
||
waar Server Up niet bij kan, met dezelfde uitleg: welke regel in `.env`, en
|
||
welk commando erna. Een ongewijzigd pad blokkeert niets, anders kun je met een
|
||
al kapotte instelling niets meer wijzigen.
|
||
- Is de lijst leeg, dan zegt het scherm nu **waarom**: niet "Geen apps
|
||
gevonden", maar dat Server Up er niet bij kan en wat eraan te doen is.
|
||
|
||
# v0.8.04-beta — `--doctor` vraagt het de app in plaats van het af te leiden
|
||
|
||
Drie keer op rij heb ik de verkeerde oorzaak aangewezen voor een lege
|
||
appslijst — een verouderde koppeling, submounts, ontbrekende compose-bestanden —
|
||
omdat `--doctor` niet genoeg vertelde om ertussen te kiezen. Hij telde regels
|
||
met `ls`, en dat zegt niets over wat de app zelf ziet.
|
||
|
||
Nu vraagt hij het de app, en toont:
|
||
|
||
- het pad **letterlijk** (`repr`), dus met een spatie of regeleinde er zichtbaar
|
||
in. Zo'n onzichtbaar teken maakt een pad onvindbaar terwijl het er in een log
|
||
volkomen normaal uitziet
|
||
- of de app die map ook echt vindt — zo niet, dan is dat een fout met de
|
||
aanwijzing waar je moet kijken
|
||
- **per map of hij als app meetelt**, en zo niet: waarom. Een map zonder
|
||
compose-bestand telt niet mee, verdwijnt uit de lijst, en laat zijn containers
|
||
onder "handmatig gestart" belanden
|
||
|
||
Dat is het verschil tussen "Server Up ziet geen enkele stack" en weten wélke map
|
||
om welke reden afvalt.
|
||
|
||
# v0.8.03-beta — Mappen zonder compose-bestand verdwenen geruisloos
|
||
|
||
`GET /api/stacks` sloeg elke map zonder compose-bestand over met een kale
|
||
`continue`. Staat er zo'n map in je stacks-map, dan duikt hij nergens meer op:
|
||
niet in de lijst, en de containers die erbij horen belanden onder "handmatig
|
||
gestart" omdat ook `beheerd` op datzelfde compose-bestand afgaat. Je ziet een
|
||
leeg scherm, en nergens staat waarom.
|
||
|
||
De interface hééft er een weergave voor — rode stip, een streepje in plaats van
|
||
een status, geen knoppen — maar die kon door dat `continue` nooit te zien zijn.
|
||
Dode weergavecode voor een toestand die de API niet kon opleveren.
|
||
|
||
Zulke mappen staan er nu gewoon tussen, mét die waarschuwing. Beter een app die
|
||
zegt dat er iets mis is dan een app die verdwijnt.
|
||
|
||
# v0.8.02-beta — Koppelingen ónder BASE_DIR
|
||
|
||
`--doctor` zei dat `/opt/serverup` netjes gekoppeld was, en dat klopte ook.
|
||
Alleen zag Server Up er geen enkele app, terwijl er op de host drie stonden.
|
||
|
||
De verklaring zit een niveau dieper. Docker bindt met propagation `rprivate`:
|
||
een koppeling die op de host **ná** het aanmaken van de container ontstaat, komt
|
||
daar niet doorheen. Is `stacks` (of `appdata`, of `backups`) een eigen mount —
|
||
een aparte dataset, een tweede schijf, een netwerkmap — dan ziet de container de
|
||
lege map eronder. De koppeling van `BASE_DIR` zelf is intact, dus daar valt
|
||
niets aan op te merken; het scherm blijft alleen leeg.
|
||
|
||
`--doctor` loopt nu met `findmnt` alle koppelingen onder `BASE_DIR` langs en
|
||
vergelijkt ze met wat de container in zijn eigen mountlijst heeft. Ontbreekt er
|
||
een, dan zegt hij welke, en hoe je hem meekoppelt:
|
||
|
||
```
|
||
✖ /opt/serverup/stacks is op de host een eigen koppeling, maar niet in de container
|
||
De container ziet daar de lege map eronder. Koppel hem apart mee in
|
||
docker-compose.yml:
|
||
- /opt/serverup/stacks:/opt/serverup/stacks
|
||
```
|
||
|
||
# v0.8.01-beta — "Nog 106 tonen" onder een leeg scherm
|
||
|
||
De app store toonde geen enkele kaart, met daaronder een knop die beloofde dat
|
||
er nog 106 apps wachtten. Twee bronnen die uit elkaar konden lopen: het raster
|
||
liep over de **repolijst**, de teller over de **opgehaalde apps**. Waren die
|
||
twee het oneens, dan stond er een getal onder een leeg scherm — zonder dat er
|
||
ergens een foutmelding te zien was.
|
||
|
||
- Teller en raster komen nu uit dezelfde lijst. De knop kan niets meer beloven
|
||
wat er niet staat.
|
||
- Een lege store zegt nu wát er aan de hand is: geen repository toegevoegd, of
|
||
geen apps die aan het filter voldoen. Eerder bleef het scherm gewoon leeg.
|
||
- `GET /api/repos` gaf een 500 op een repo zonder `id`. Eén rare regel in de
|
||
configuratie maakte daarmee de hele store leeg; zulke regels worden nu
|
||
overgeslagen.
|
||
|
||
# v0.8.00-beta — Een kapotte cache herstelt zichzelf
|
||
|
||
`fatal: shallow file has changed since we read it` liet een repo achter die zich
|
||
niet meer liet bijwerken — en daarmee een app store die leeg bleef.
|
||
|
||
Die melding komt van twee `git fetch` in dezelfde map. Bij het opstarten
|
||
synchroniseert Server Up alles, en ondertussen kan je op **Synchroniseren**
|
||
drukken of een app installeren die de repo ook aanraakt. Er zat geen slot op:
|
||
elke synchronisatie kon over een andere heen lopen en de cache halverwege
|
||
achterlaten, met een `.git/shallow` waar git niets meer mee kan.
|
||
|
||
Twee dingen erbij:
|
||
|
||
- **Eén git tegelijk per repo.** Botsen kan nu niet meer.
|
||
- **Zelfherstel.** Lukt het bijwerken niet — om welke reden dan ook — dan wordt
|
||
de cache weggegooid en opnieuw opgehaald. Er staat niets in dat bewaard hoeft
|
||
te blijven, dus dat is het juiste antwoord op élke fout hier, en niet alleen
|
||
op deze. Een repo die zich niet meer laat bijwerken kan daarmee niet meer
|
||
bestaan.
|
||
|
||
Dat repareert ook een cache die al stuk is: de eerstvolgende synchronisatie
|
||
haalt hem gewoon opnieuw op.
|
||
|
||
# v0.7.99-beta — "Gekoppeld" zei niets
|
||
|
||
`--doctor` meldde vrolijk dat `/opt/serverup` in de container gekoppeld was,
|
||
terwijl Server Up daar geen enkele app zag en er buiten de container drie
|
||
stonden. De controle keek of het pad in `/proc/self/mountinfo` voorkwam, en dat
|
||
is precies de verkeerde vraag.
|
||
|
||
Een bind mount wijst naar een **inode**, niet naar een pad. Wordt de map op de
|
||
host vervangen nadat de container is aangemaakt — verwijderd en opnieuw
|
||
aangemaakt — dan blijft de container naar de oude, losgekoppelde map kijken.
|
||
Meestal is die leeg. Het pad staat dan nog gewoon in de mountlijst, dus
|
||
"gekoppeld" blijft er stralend staan terwijl je hele appslijst leeg is.
|
||
|
||
`--doctor` telt nu aan beide kanten en vergelijkt:
|
||
|
||
- bestaat de map binnen de container überhaupt?
|
||
- staan er buiten wél apps en binnen niet? Dan is de koppeling verouderd, en
|
||
staat erbij hoe je hem herstelt (`docker compose up -d --force-recreate`)
|
||
- is hij aan beide kanten leeg, dan is er simpelweg nog niets geïnstalleerd —
|
||
een waarschuwing, geen fout
|
||
|
||
# v0.7.98-beta — Het heet nu gewoon een app
|
||
|
||
Je installeerde een **app** uit de **App Store** en kreeg een **stack**. Dat
|
||
moest je maar net weten. In de interface heet alles nu een app: 24 teksten per
|
||
taal plus de terugvalteksten in de pagina zelf.
|
||
|
||
De code, de API-paden (`/api/stacks/…`), de mapnamen en `.serverup.json` houden
|
||
`stack`. Dat zijn geen woorden die iemand leest, en ze hernoemen zou opgeslagen
|
||
gegevens en URL's breken zonder dat er iets duidelijker van wordt.
|
||
|
||
**De tweede lijst is verdwenen.** "Overige containers" stond als aparte sectie
|
||
onderaan, met een eigen kop, een eigen uitleg en een eigen vormgeving. Wie zijn
|
||
apps kwijt was moest weten dat die lijst bestond. Handmatig gestarte containers
|
||
staan nu gewoon tussen de rest, als kaart, met het label **Handmatig gestart**.
|
||
Er is minder mee te doen — starten, stoppen, logs — maar ze zijn wel te vinden.
|
||
De zoekterm werkt op allebei; een categoriefilter laat ze vallen, want ze hébben
|
||
geen categorie.
|
||
|
||
Onderweg gevonden: de waarschuwing bij backups zei nog dat een backup *"niet de
|
||
Docker-volumes met de eigenlijke appdata"* bevat. Sinds v0.7.88 zit de appdata er
|
||
juist wél in, met een dump van elke database erbij. Die tekst klopt nu.
|
||
|
||
# v0.7.97-beta — Repo's bijwerken werkte niet meer
|
||
|
||
**`fatal: Cannot rebase onto multiple branches.`** Het bijwerken van een
|
||
app-repo gebruikte `git pull --rebase --autostash`, en dat commando leunt op de
|
||
upstream-instellingen van de lokale kloon. Staan er meerdere merge-doelen in
|
||
`branch.<tak>.merge`, dan stopt git ermee en werkt er niets meer bij — ook de
|
||
app store niet, want die leest uit diezelfde cache.
|
||
|
||
Bij het naspelen bleek er een tweede fout in dezelfde regel te zitten: **`git
|
||
pull` zonder argumenten haalt de tak op waarop ooit gekloond is.** De branch uit
|
||
de repo-instellingen werd dus genegeerd. Zette je die van `main` naar `dev`, dan
|
||
bleef je stilzwijgend `main` krijgen.
|
||
|
||
De cache is van Server Up zelf; er staat niets in dat bewaard hoeft te blijven.
|
||
Het bijwerken haalt nu de ingestelde tak op en zet de cache daar hard op. Dat is
|
||
ongevoelig voor ontspoorde upstream-instellingen, volgt een gewijzigde branch
|
||
wél, en laat zich niet blokkeren door lokale wijzigingen in de cache.
|
||
|
||
**`--doctor` vraagt Server Up nu zelf naar zijn paden.** Wat er op de host staat
|
||
is één ding; waar de app kijkt is een ander, en die twee kunnen uit elkaar lopen
|
||
— een `LIBRARY_DIR` uit een oude configuratie wint van de standaard. Dan zie je
|
||
een lege stacklijst zonder dat ergens staat waarom. Hij toont nu welke
|
||
`LIBRARY_DIR` en `DATA_DIR` de app gebruikt, hoeveel stacks hij daar ziet, en
|
||
hoeveel apps er in de store-cache zitten.
|
||
|
||
# v0.7.96-beta — Backups eindelijk écht getest, en terugrollen
|
||
|
||
**Backups waren nooit tegen een echte Docker-daemon gedraaid.** Alle
|
||
backuptests vervangen Docker door een nepversie — vijftien keer in
|
||
`test_backups.py` alleen al. Dat toetst de logica maar niet de keten waar het om
|
||
gaat, en juist dáár betekent fout zijn gegevensverlies.
|
||
|
||
`tools/backup_rondgang.py` draait die keten nu wel echt, in de CI, op het image
|
||
dat ook in productie draait: een stack met een postgres-sidecar starten en
|
||
vullen, backuppen, de tabel en de appdata weggooien, terugzetten, en controleren
|
||
dat de rij er weer staat én dat de database nog kan schríjven. Dat laatste is de
|
||
vraag waar het om draait — een datamap van de verkeerde eigenaar geeft pas bij
|
||
de eerste schrijfactie een fout.
|
||
|
||
Dat bracht meteen een fout aan het licht die de unittests niet konden zien:
|
||
**het terugzetten gaf de appdata de verkeerde eigenaar.** `tarfile` met
|
||
`filter="data"` laat uid en gid vallen, en de terugval was de `PUID` uit de
|
||
metadata — of anders onze eigen uid. Maar een postgres-container draait als uid
|
||
70 en zegt dat nergens in de metadata. Het archief wéét het wel: die uid staat
|
||
er gewoon in. Die gaat nu voor, met de `PUID` als tweede en onze eigen uid als
|
||
laatste antwoord.
|
||
|
||
**Een mislukte `--update` rolt nu terug.** De commit van vóór het bijwerken
|
||
wordt onthouden; komt de nieuwe versie niet omhoog, dan gaat de broncode terug
|
||
en start die opnieuw. Je houdt geen stilstaande server over en geen rijtje
|
||
commando's dat je zelf moet uitvoeren.
|
||
|
||
**Installeren gaat sneller als er een registry-image is.** Staat er een
|
||
`SU_IMAGE` met een registrypad in `.env`, dan haalt het script dat op in plaats
|
||
van zelf te bouwen — dat scheelt de paar minuten die een build kost. Zonder
|
||
blijft het bouwen, want dan bestaat het image nergens anders.
|
||
|
||
**Nieuw: `--reconfigure`.** Bind-adres, poort, account of hoofdmap wijzigen
|
||
zonder de broncode aan te raken. Daarvoor moest je `--update` draaien, en dan
|
||
haalde je ongewild ook nieuwe code binnen.
|
||
|
||
**Nieuw: `--uninstall --purge`.** Verwijdert ook het `su-data`-volume, je
|
||
gegevensmap en het aangemaakte account — per onderdeel apart gevraagd, met de
|
||
omvang van de gegevensmap erbij. Zonder `--purge` somt `--uninstall` nu op wat
|
||
er blijft staan, inclusief het account, in plaats van dat te verzwijgen.
|
||
|
||
Verder:
|
||
|
||
- `--dir` wordt net als `--base-dir` op een volledig pad gecontroleerd. Met
|
||
`curl … | sh` is de werkmap je thuismap, en dit pad gaat letterlijk naar
|
||
compose.
|
||
- Het tijdelijke bestand voor het aanmaken van de beheerder viel terug op
|
||
`/tmp/su-setup.$$` als `mktemp` faalde. Die naam is voorspelbaar en `/tmp` is
|
||
voor iedereen schrijfbaar.
|
||
- `controleer_docker` deed twee rondgangen naar de daemon (`docker info` én
|
||
`docker version`) waar er één volstaat.
|
||
|
||
# v0.7.95-beta — Bijwerken vanuit de interface, en een script dat nakijkt
|
||
|
||
Het tweede deel van de controle op het installatiepad.
|
||
|
||
**Bijwerken vanuit de interface kon nooit werken.** `selfupdate` schreef
|
||
`SU_TAG` naar `<werkmap>/.env` — maar die map is nergens in de container
|
||
gemount. Er zijn maar drie mounts: de docker-socket, het `su-data`-volume en
|
||
`BASE_DIR`. De installatiemap zit daar niet bij, dus schrijven mislukte. Dat
|
||
viel niet op omdat een zelfgebouwd image de knop toch al uitschakelt; zette je
|
||
`SU_IMAGE` naar een registry — de manier die `docs/updates.md` noemt om hem aan
|
||
te zetten — dan faalde hij altijd. De helper-container mount die map wél en
|
||
draait als root, dus die zet de tag nu.
|
||
|
||
**Een vastgepinde versie kon nooit bijgewerkt worden.** `--branch v0.7.92-beta`
|
||
installeerde prima, maar bijwerken deed `git reset --hard origin/v0.7.92-beta`
|
||
en tags krijgen geen `origin/`-ref: "fatal: ambiguous argument". Het resetten
|
||
gaat nu naar `FETCH_HEAD`, wat voor takken en tags allebei klopt. De test liet
|
||
zien dat het na een tag-installatie óók voor takken stuk was.
|
||
|
||
**De token stond in `.git/config` en in `ps`.** In de URL schrijft git hem
|
||
verbatim in de config, waar hij blijft staan; als argument staat hij in
|
||
`/proc/<pid>/cmdline` en leest elke gebruiker op de server hem met `ps`. Dat is
|
||
precies waarom dit script `--admin-password` weigert. Hij gaat nu via een
|
||
bestand met modus 0600 dat na afloop weer weg is — git leest het via een
|
||
credential-helper, curl via `--config`; alleen het pad staat op de opdrachtregel.
|
||
|
||
Twee nieuwe opties:
|
||
|
||
- **`--base-dir PAD`** zet de hoofdmap voor stacks, appdata en backups bij de
|
||
installatie, in plaats van achteraf in `.env` — waarna je de data alsnog moet
|
||
verhuizen. Relatieve paden en systeemmappen worden geweigerd.
|
||
- **`--doctor`** licht een bestaande installatie door zonder iets te wijzigen:
|
||
container, bereikbaarheid *op het ingestelde adres*, onder welk account hij
|
||
draait versus wat er in `.env` staat, of `BASE_DIR` echt gekoppeld is (bestaan
|
||
is niet genoeg), de rechten van `.env`, en hoeveel ruimte er over is. Exitcode
|
||
1 bij fouten. De CI draait hem na elke deploy tegen de echte installatie —
|
||
ruim honderd regels shell die je nergens anders getest krijgt.
|
||
|
||
# v0.7.94-beta — Het installatiescript loog op drie plekken
|
||
|
||
Een controle van het hele installatiepad leverde drie fouten op die alledrie op
|
||
`curl … | sh` liggen — de regel die de README noemt.
|
||
|
||
**Vragen werden niet gesteld en beantwoordden zichzelf met "ja".** `vraag()`
|
||
keek naar stdin, en bij `curl … | sh` is dat de pipe. Alle andere prompts in het
|
||
script gebruiken `/dev/tty`, dat gewoon bereikbaar is. Gevolg: "Nu installeren
|
||
via het officiële script van docker.com?" werd overgeslagen en met ja
|
||
beantwoord, `--uninstall` brak je installatie af zonder bevestiging, en de vraag
|
||
of je een beheerdersaccount wilde bleef ongesteld terwijl de vervolgprompt
|
||
("Gebruikersnaam:") wél verscheen. Er is nu één `terminal_beschikbaar()` die
|
||
alle vijf de plekken gebruiken.
|
||
|
||
**Met een eigen bind-adres mislukte de healthcheck én het beheerdersaccount.**
|
||
Compose publiceert op `$BIND`, maar het script vroeg altijd `127.0.0.1`. Met
|
||
`--bind 10.0.20.5` luistert daar niets, dus meldde het "reageerde niet binnen
|
||
anderhalve minuut" en daarna "Server Up was niet bereikbaar; account niet
|
||
aangemaakt" — terwijl alles draaide. Je hield een installatie over zonder
|
||
account, precies het venster waar het script voor waarschuwt. Het controleadres
|
||
volgt nu `$BIND`, met blokhaken om IPv6.
|
||
|
||
**Een mislukte update meldde zich als geslaagd.** `wacht_op_gereed || true`
|
||
gevolgd door `goed "Bijgewerkt."` maakte een kapotte update niet te
|
||
onderscheiden van een goede, ook niet aan de exitcode. Nu stopt hij met een
|
||
foutmelding en de weg terug. Bij een verse installatie gaat hij wél door — die
|
||
aanwijzingen zijn juist dán nuttig — maar het slot zegt eerlijk dat er niet
|
||
geantwoord is en de exitcode is 1.
|
||
|
||
Verder:
|
||
|
||
- `curl` kreeg tijdslimieten bij het pollen. Zonder die limieten bleef hij
|
||
hangen op een adres waar niets naartoe routeert en betekende het aantal
|
||
pogingen niets. Het aantal is nu ook instelbaar met `SU_WACHT_POGINGEN`, voor
|
||
trage machines.
|
||
- Het slot zei nog "Server Up beheert Docker als root" en noemt nu onder welk
|
||
account het draait.
|
||
|
||
Onderweg gevonden en meteen verholpen: de eerste versie van
|
||
`terminal_beschikbaar()` gebruikte `{ : </dev/tty; }`. `:` is een *special
|
||
builtin*, en een mislukte redirect daarop beëindigt volgens POSIX de hele shell
|
||
— exit 2, zonder melding, nog vóór de eerste stap. Dat brak elke installatie
|
||
zonder terminal. De openpoging staat nu in een subshell.
|
||
|
||
# v0.7.93-beta — Server Up draait niet meer als root
|
||
|
||
Er stond `user: "0:0"` in `docker-compose.yml` en nergens waarom. Dat is nu een
|
||
keuze die je bij de installatie maakt.
|
||
|
||
**Bij het installeren wordt gevraagd onder welk account het moet draaien**: een
|
||
nieuw systeemaccount `serverup` (aanbevolen, wordt aangemaakt zonder shell en
|
||
zonder wachtwoord), het account waarmee je werkt, een bestaand account uit een
|
||
lijst, of root zoals voorheen. Met `--user NAAM` of `--yes` sla je de vraag over.
|
||
Een bestaande installatie krijgt de vraag één keer bij `--update`; daarna blijft
|
||
je keuze staan.
|
||
|
||
**De container start nog steeds als root en zakt daarna af.** Dat moet: Docker
|
||
maakt het `su-data`-volume als root aan, en een container die meteen als een
|
||
gewone gebruiker start komt niet eens tot zijn configuratie. Het nieuwe
|
||
`docker-entrypoint.sh` zet `/data`, `stacks` en `backups` klaar en gebruikt dan
|
||
`setpriv` — niet `gosu`, want een proces dat met `setuid` afzakt verliest zijn
|
||
capabilities, en die zijn hier nodig. `appdata` blijft ongemoeid: die mappen zijn
|
||
van de apps zelf, en een `chown` daaroverheen breekt precies de containers die
|
||
als hun eigen uid draaien.
|
||
|
||
Het afgezakte proces houdt `CHOWN`, `DAC_OVERRIDE` en `FOWNER`. Alle drie zitten
|
||
al in de standaardset van Docker, dus de container krijgt geen enkel recht bij —
|
||
er gaan er alleen af. Wat dat wél en níét oplevert staat in
|
||
`docs/beveiliging.md`; kort gezegd beschermt het tegen bugs en ongelukken, niet
|
||
tegen iemand die de interface overneemt. Die heeft de docker-socket, en dat
|
||
blijft root. Een socket-proxy helpt daar niet tegen, omdat het dóél van deze app
|
||
is om containers met willekeurige bind mounts aan te maken.
|
||
|
||
**Twee rechten-fouten die hierdoor aan het licht kwamen, zijn ook verholpen:**
|
||
|
||
- Het installatieformulier zette `PUID`/`PGID` op wat het sjabloon toevallig
|
||
noemde — 44 sjablonen staan blind op `1000`. Nu krijgen ze de ids waaronder
|
||
Server Up draait, zodat appdata van hem is en hij hem kan inpakken.
|
||
- Eén onleesbaar bestand liet de héle backup falen: de `tar.add` van de
|
||
appdata-map zat in dezelfde `try` als de rest. Nu gaat de rest gewoon mee en
|
||
komt wat ontbreekt in het log én in de metadata (`skipped`) te staan. Een
|
||
archief dat compleet lijkt maar het niet is, is erger dan een archief dat zegt
|
||
wat het mist.
|
||
- Terugzetten gaf alles aan wie uitpakte: `tarfile` met `filter="data"` (PEP 706)
|
||
laat uid en gid uit het archief vallen. De appdata krijgt nu de `PUID`/`PGID`
|
||
uit de metadata terug, anders die van Server Up zelf.
|
||
|
||
De CI controleert vanaf nu in een echte container dat `setpriv` bestaat, dat er
|
||
wordt afgezakt, dat de gid van de docker-socket meekomt, dat `/data` schrijfbaar
|
||
is, en dat `SU_UID=0` root laat blijven.
|
||
|
||
**Terug naar de oude situatie:** zet `SU_UID=0` en `SU_GID=0` in je `.env` en
|
||
draai `docker compose up -d`.
|
||
|
||
# v0.7.92-beta — Eén hoofdmap, en je data mag mee verhuizen
|
||
|
||
**Server Up stelde `/opt/serverup` voor, wat je ook instelde.** Dat was geen
|
||
weergavefout maar drie fouten tegelijk:
|
||
|
||
- `LIBRARY_DIR`, `DATA_DIR` en `BACKUP_DIR` stonden hard op `/opt/serverup/*` en
|
||
trokken zich niets aan van de `BASE_DIR` die `docker-compose.yml` wél kent. Ze
|
||
worden nu afgeleid van `BASE_DIR`, en compose geeft die variabele voortaan ook
|
||
aan de container door — die stond alleen in de mount, dus je kon `/srv`
|
||
koppelen terwijl de app `/opt/serverup` bleef voorstellen.
|
||
- 266 van de 286 sjablonen hebben `/opt/serverup/appdata` als standaard voor
|
||
`appdata_dir`. Het installatieformulier overschreef wel de tijdzone met die
|
||
van de server, maar niet de appdata-map. Wat je ook had ingesteld, je data
|
||
belandde in `/opt`. Dat is nu één plek in `_annotate_fields()`, en daarmee
|
||
meteen goed voor alle sjablonen.
|
||
- `.env.example` en de documentatie noemden `/opt/serverup` als vast gegeven.
|
||
|
||
**Instellingen → Paden werkt nu met één hoofdmap.** Typ je er een, dan verschijnen
|
||
de drie submappen eronder als voorstel, elk met een knop om hem over te nemen, en
|
||
één knop **Alles overnemen**. De velden blijven los aanpasbaar. `/api/paths/check`
|
||
controleert onderweg of Server Up er überhaupt bij kán: de container ziet alleen
|
||
wat via `BASE_DIR` gekoppeld is, en daarbuiten schrijft hij naar zijn eigen
|
||
overlay — dat lijkt te lukken en levert daarna lege apps op. Ligt je map erbuiten,
|
||
dan noemt de melding de regel voor `.env` en het commando, in plaats van "kon niet
|
||
opslaan".
|
||
|
||
**Een gewijzigd pad biedt aan de data mee te verhuizen.** Alleen de instelling
|
||
aanpassen liet je data staan waar hij stond, en elke stack wees nog naar zijn oude
|
||
appdata-map. De verhuizing stopt de draaiende stacks, kopieert met een
|
||
voortgangsbalk, controleert aantal en omvang, schrijft de paden om in
|
||
`docker-compose.yml`, `.env` en de metadata van elke stack, start de stacks weer
|
||
op de nieuwe plek — **en ruimt pas daarna het oude op**. Klopt de controle niet,
|
||
dan blijft het origineel staan en is de melding de fout. Het omschrijven kijkt
|
||
naar hele pad-segmenten, zodat `/opt/serverup` vervangen `/opt/serverup-oud` met
|
||
rust laat.
|
||
|
||
**Rechten én eigenaar gaan mee.** `shutil.copy2` neemt de rechten over maar niet
|
||
de eigenaar, en Server Up draait als root — verhuisde appdata werd dus root:root,
|
||
waarna een container die als PUID 1000 draait niet meer bij zijn eigen gegevens
|
||
kon. De linuxserver-images zetten `/config` bij het starten terug, maar postgres,
|
||
Nextcloud en Immich lopen er gewoon op vast. De verhuizing chownt nu elk bestand,
|
||
elke map, elke symlink én de hoofdmap zelf naar de eigenaar van het origineel.
|
||
Lukt dat niet, dan gaat de verhuizing door met een waarschuwing in het log.
|
||
|
||
Jobs kregen daarvoor een `voortgang()`; de terminal toont de balk boven het log.
|
||
|
||
# v0.7.91-beta — "Poort {port} is vrij" stond er letterlijk
|
||
|
||
De poortcontrole werd aangesloten op vertaalsleutels die er al waren, en twee
|
||
daarvan verwachten een plaatshouder die niet werd meegegeven: `port_free` wil
|
||
`{port}`, `port_in_use` wil `{port}` én `{by}`. In beeld stond dus letterlijk
|
||
"Poort {port} is vrij".
|
||
|
||
Er draait nu een test die elke `t('sleutel', { … })` in de interface naloopt en
|
||
vergelijkt met de plaatshouders in de Nederlandse tekst — in beide richtingen,
|
||
en ook voor aanroepen zónder variabelen. Dat laatste was nodig: de eerste versie
|
||
van die test keek alleen naar aanroepen die wél iets meegaven, en liet de fout
|
||
waar hij voor gemaakt was gewoon passeren.
|
||
|
||
# v0.7.90-beta — Poorten kiezen zonder gokken, en installeren op een telefoon
|
||
|
||
**De poortcontrole zat er wel, maar werd nooit gevraagd.** `/api/ports/check`
|
||
bestaat en kijkt naar wat Docker publiceert én naar wat er rechtstreeks op de
|
||
host luistert — dat laatste is precies de botsing die je anders pas ziet als de
|
||
stack niet start. Het installatieformulier riep hem alleen nergens aan. Er stond
|
||
hooguit een regeltje als de stándaardwaarde bezet was; typte je zelf iets, dan
|
||
hoorde je niets meer.
|
||
|
||
Een poortveld heeft nu knoppen om een stapje omhoog of omlaag te doen, een
|
||
vergrootglas om te controleren, en een melding eronder: vrij, of bezet en door
|
||
welke container, met een knop om meteen naar de eerstvolgende vrije poort te
|
||
springen. De controle loopt ook zodra je een stap opent, dus je ziet het vóórdat
|
||
je iets typt. Kan de host niet uitgelezen worden, dan zegt de melding dat "vrij"
|
||
een aanname is in plaats van het te verzwijgen.
|
||
|
||
De knoppen zitten alleen op velden die ook echt een poort zijn — `puid` en `pgid`
|
||
zijn ook getallen. En het scherm *Instellingen wijzigen* van een bestaande stack
|
||
deed de controle bij het openen niet mee; dat is hetzelfde formulier, dus dat
|
||
hoort er ook bij.
|
||
|
||
**Installeren op een telefoon.** De installatiemodal is daar nu schermvullend in
|
||
plaats van een venstertje met marges, met de kop en de knoppenbalk vast en alleen
|
||
de inhoud die scrollt. Vorige en Volgende vullen samen de breedte in plaats van
|
||
op een rare plek af te breken. En omdat er dan geen achtergrond meer is om op te
|
||
tikken, zit er een sluitknop in de kop — die was er niet, en zonder deze
|
||
wijziging was je er niet uit gekomen.
|
||
|
||
# v0.7.89-beta — De store blijft soepel, en achtentwintig apps erbij
|
||
|
||
**Typen in het zoekveld werd traag.** `filteredStore()` is een methode, geen
|
||
gecachte getter, en werd per repo drie keer per reactieve tick aangeroepen: in de
|
||
`x-show`, in de `x-for` en via de teller in het filtermenu. Elke aanroep liep alle
|
||
apps langs met een `toLowerCase()` op naam, map, omschrijving en elke tag — bij
|
||
iedere toetsaanslag opnieuw. De catalogus ging deze reeks van 142 naar 286 apps,
|
||
en dat begon te merken.
|
||
|
||
Het filteren gebeurt nu één keer per filterstand en wordt vastgehouden tot je iets
|
||
verandert. Daarnaast staan er nog maar zestig kaarten tegelijk in beeld, met een
|
||
knop **Nog N tonen** eronder; die limiet valt terug zodra je het filter of de
|
||
zoekterm wijzigt, anders sta je na één zoekactie voor de rest van de sessie naar
|
||
alles te kijken. En het zoekveld wacht 200 ms voordat het filtert.
|
||
|
||
**Achtentwintig apps** in de hoeken die het dunst waren:
|
||
|
||
- **Beveiliging:** 2FAuth (tweestapscodes naast je kluis, niet erin), Passbolt
|
||
(wachtwoorden delen in een team), OpenBao, Trivy
|
||
- **Ontwikkeling:** Verdaccio, Docker Registry, MkDocs Material
|
||
- **AI:** LiteLLM, Flowise, Speaches
|
||
- **Spellen:** Pelican Panel, Gameyfin, EmulatorJS
|
||
- **3D-printen:** Mainsail en Fluidd — de twee interfaces voor Klipper, waar
|
||
OctoPrint niets mee doet
|
||
- **Productiviteit:** Kanboard, Wekan, Filestash
|
||
- **Documenten:** Linkwarden (bladwijzers mét een gearchiveerde kopie), Slash,
|
||
RSSHub
|
||
- **Communicatie:** Apprise (één API naar honderd meldingsdiensten), Mailpit
|
||
(vangt de mail van je eigen apps op)
|
||
- **Verder:** Dashy, Plausible, NetBird, LibreSpeed, PinePods
|
||
|
||
Alle 318 images in de catalogus zijn opnieuw tegen hun registry gecontroleerd.
|
||
|
||
# v0.7.88-beta — Backups bevatten nu je gegevens, en Pangolin hoort erbij
|
||
|
||
**Een backup gaf je niets terug.** `backups.create()` maakte een tar van de
|
||
stackmap: de compose, de `.env` en de metadata. `LIBRARY_DIR` is
|
||
`/opt/serverup/stacks` en de appdata staat in `/opt/serverup/appdata` — buren,
|
||
geen genestelde mappen. Je maakte dus een backup, kreeg een groen vinkje, en
|
||
hield bij terugzetten een lege app over. De documentatie noemde als uitweg dat je
|
||
data wél meegaat "als die map binnen je stackmap zit", maar met de
|
||
standaardinstellingen zit hij er juist buiten.
|
||
|
||
Een archief heeft nu drie takken: de stackmap, de appdata-mappen van die stack,
|
||
en een dump per database. Die dump is geen dubbelop — een tar van een draaiende
|
||
PostgreSQL is een momentopname van bestanden die tijdens het inpakken
|
||
veranderden. Bij terugzetten komt de dump erin nádat de container draait, met een
|
||
wachtlus omdat een database na het starten niet meteen verbindingen aanneemt.
|
||
Stond de container tijdens de backup uit, dan staat dat als waarschuwing in het
|
||
log; stil overslaan zou het ergste geval opleveren, een archief dat compleet
|
||
lijkt.
|
||
|
||
Nieuwe instelling **Appdata meenemen** (`BACKUP_APPDATA`, standaard aan). Uit
|
||
zetten is nodig bij een mediabibliotheek of fotoarchief: daar loopt dit in de
|
||
honderden gigabytes en is restic of borg het betere middel.
|
||
|
||
**Pangolin en Newt staan nu in de store.** Server Up had al een complete
|
||
Pangolin-integratie — een instellingenpagina, een publiceerknop per stack — maar
|
||
je moest Pangolin zelf ergens anders vandaan halen. Nu staan de tunnelclient
|
||
(Newt) en de server zelf (pangolin + gerbil + traefik, met de configuratie erbij)
|
||
in de catalogus.
|
||
|
||
**En er is een stap voor in de setup-wizard**, na de git-stap: koppelen aan een
|
||
Pangolin die je al hebt, er hier een neerzetten, of overslaan. Bij de eerste
|
||
keuze kun je meteen Newt erbij installeren. Terzijde: de wizard hing zijn
|
||
afhandeling aan stapnummers, dus met een stap ertussen ging de git-stap mis. Die
|
||
vergelijkingen gaan nu op de naam van de stap.
|
||
|
||
**Waarschuwing op de stackpagina.** `depends_on` meldde alleen bij het
|
||
installeren wat er ontbrak. Haal je Mosquitto later weg, dan viel Zigbee2MQTT
|
||
stil zonder dat iets zei waarom. Dat staat nu als badge op de stackkaart, met de
|
||
reden in de tooltip en een doorklik naar de store.
|
||
|
||
# v0.7.87-beta — Leesbare namen, zichtbaar wat al draait, en bruikbaar op een telefoon
|
||
|
||
**Het auditlog toonde ruwe id's.** In de kolommen stond `store`, `reconfigure`,
|
||
`credentials_view` — de sleutels waarmee de code logt, niet iets wat je leest. Die
|
||
hebben nu Nederlandse namen ("App Store", "Opnieuw ingesteld", "Inloggegevens
|
||
bekeken"), ook in de filterlijsten erboven, en de kolomkoppen zijn niet langer
|
||
hardgecodeerd Nederlands maar vertaald. Een categorie die we niet kennen — van een
|
||
module bijvoorbeeld — valt terug op de ruwe waarde in plaats van leeg te blijven.
|
||
Er draait een test die de broncode afloopt en elke `audit.log(...)` zonder
|
||
vertaling aanwijst. Een modulepagina heette in de titelbalk letterlijk
|
||
`mod:mijnmodule`; die toont nu de naam van de module.
|
||
|
||
**De store zag niet dat een app al draaide.** Er zat een klein groen vinkje naast
|
||
de installeerknop, en dat werkte alleen als je de stack precies zo had genoemd als
|
||
het sjabloon: `sonarr` of `sonarr-iets`. Noemde je hem `media-tv`, dan bleef de
|
||
store volhouden dat Sonarr nog niet geïnstalleerd was. Nu leest hij `.serverup.json`
|
||
uit — dezelfde `source` die het herconfigureerscherm al gebruikte — en toont een
|
||
duidelijk **Geïnstalleerd**-label met de naam van elke draaiende installatie,
|
||
waar je op kunt klikken om er meteen naartoe te gaan. De knop eronder zegt dan
|
||
"Nog een keer" in plaats van "Installeren".
|
||
|
||
**Op een telefoon was de stackpagina niet te doen.** Elke stack had twaalf
|
||
icoonknoppen zonder tekst naast elkaar, wat op een smal scherm drie rijen
|
||
plaatjes werd. Daar staan nu starten, herstarten en logs, plus een menu met de
|
||
rest — mét namen erbij. Op een breed scherm verandert er niets. De rij van twintig
|
||
categorieknoppen boven de stacks is hetzelfde menu geworden als in de app store,
|
||
en "Updates controleren" verliest op smalle schermen zijn label.
|
||
|
||
# v0.7.86-beta — De filter in de app store is een menu geworden
|
||
|
||
**Twee rijen knoppen vulden een half scherm.** Boven de app store stonden twintig
|
||
categorieknoppen en veertien labelknoppen, allemaal tegelijk uitgeklapt. Bij 256
|
||
apps is dat geen filter meer maar een muur waar je langs moet scrollen voordat je
|
||
de eerste app ziet.
|
||
|
||
Het is nu één knop **Filter** met een menu eronder. Categorieën staan in een
|
||
raster van twee kolommen met hun eigen kleur en het aantal apps erachter, labels
|
||
hebben een eigen zoekveld, en onderaan staat hoeveel apps er overblijven met een
|
||
knop om alles in één keer te wissen. Het menu gaat dicht als je ernaast klikt of
|
||
op Escape drukt.
|
||
|
||
**En de labelfilter werkte maar half.** De rij was afgekapt op veertien labels,
|
||
terwijl er 297 in de catalogus zitten. De overige 283 waren gewoon niet te kiezen
|
||
— je zag ze wel op de app-kaarten staan, maar erop filteren kon niet. Het menu
|
||
zoekt nu in de volledige lijst; zonder zoekterm toont het de veertig meest
|
||
gebruikte, en een label dat je gekozen hebt blijft zichtbaar ook als het zeldzaam
|
||
is.
|
||
|
||
**Wat je aan hebt staan zie je zonder het menu te openen:** actieve filters staan
|
||
als chips naast de knop, met een kruisje om ze los weg te halen. En het zoekveld
|
||
heeft een wisknop gekregen.
|
||
|
||
**Nieuwe test op de HTML zelf.** De interface is één sjabloon van ruim
|
||
drieduizend regels met Alpine-templates die in elkaar zitten. Eén `</div>`
|
||
verkeerd en de rest van de pagina schuift in de verkeerde tak — wat je pas merkt
|
||
als je die kant op klikt. Er draait nu een test die het hele sjabloon op
|
||
gebalanceerde tags controleert, naast de bestaande die de JavaScript parseert.
|
||
|
||
# v0.7.85-beta — Sjablonen kunnen zeggen wat ze nodig hebben, en vijftien domotica-apps
|
||
|
||
**Een app kon niet zeggen dat hij een andere nodig had.** Het `connect`-veld
|
||
wijst naar een andere app en vult een adres in, maar zegt niets over noodzaak.
|
||
Daardoor kon je Zigbee2MQTT installeren zonder MQTT-broker, Alertmanager zonder
|
||
Prometheus en Grafana Alloy zonder Loki. Alle drie starten ze, doen niets, en de
|
||
reden staat nergens.
|
||
|
||
Sjablonen kunnen dat nu wél zeggen, met `depends_on` in de metadata. Het
|
||
installatiescherm toont vóór het invullen wat er ontbreekt, met de reden erbij en
|
||
een knop die je meteen naar dat sjabloon brengt. **Advies, geen blokkade** — je
|
||
kunt de broker of Home Assistant net zo goed op een andere machine hebben staan.
|
||
De reden is verplicht: een waarschuwing zonder uitleg klik je weg.
|
||
|
||
Meteen toegepast op acht sjablonen waar het gemis tot nu toe onzichtbaar was:
|
||
Zigbee2MQTT, Alloy, Alertmanager, node-exporter, cAdvisor, Collabora, Double Take
|
||
en Soularr. Bewust níét op Element, open-webui, Exportarr, Unpackerr en ONLYOFFICE:
|
||
die werken met meerdere alternatieven, en `depends_on` kent geen "een van deze".
|
||
|
||
**Vijftien domotica-apps.** Van 12 naar 27 in die categorie — vooral het spul dat
|
||
*om* Home Assistant heen hoort:
|
||
|
||
- **Stemassistent**, tot nu toe volledig afwezig: Piper (tekst naar spraak),
|
||
Whisper (spraak naar tekst) en openWakeWord (het activeerwoord). Samen maken ze
|
||
de spraakbediening van HA compleet zonder dat er iets naar een clouddienst gaat.
|
||
- **Matter Server** en **Matter Hub**, plus **AppDaemon** voor automatiseringen in
|
||
Python
|
||
- **go2rtc** (camerastreams) en **Double Take** (gezichtsherkenning op Frigate)
|
||
- **Domoticz** en **openHAB** als alternatief voor Home Assistant zelf
|
||
- Voor de Nederlandse opstelling: **DSMR-reader** leest de P1-poort van je slimme
|
||
meter uit, **rtl_433** vangt de 433 MHz-sensoren van weerstations en regenmeters
|
||
op en zet ze op MQTT
|
||
- **Traccar** (GPS-tracking), **Snapcast** (audio door het hele huis) en **EMQX**
|
||
(MQTT-broker met webinterface)
|
||
|
||
Drie daarvan hebben een fysiek apparaat nodig — een P1-kabel, een SDR-stick, een
|
||
Zigbee-dongle. Die krijgen een veld met uitleg over hoe je het juiste pad vindt,
|
||
in plaats van een gok die stilzwijgend faalt.
|
||
|
||
# v0.7.84-beta — Prometheus startte niet, en 23 apps erbij
|
||
|
||
**Prometheus stond kapot in de catalogus.** Het sjabloon koppelde een lege map op
|
||
`/etc/prometheus` en startte met `--config.file=/etc/prometheus/prometheus.yml` —
|
||
een bestand dat er nooit kwam. Prometheus stopt daar meteen op. En zelfs mét
|
||
configuratie viel er niets te meten: er was in de hele catalogus geen
|
||
node-exporter en geen cAdvisor. Er zit nu een startconfiguratie bij, met
|
||
koppelvelden naar allebei.
|
||
|
||
Datzelfde patroon zat op meer plekken, en dat is waar de meeste apps van deze
|
||
ronde vandaan komen — niet nieuwe categorieën, maar de ontbrekende helft van wat
|
||
er al stond:
|
||
|
||
- **node-exporter**, **cAdvisor** en **Alertmanager** maken Prometheus en Grafana
|
||
pas bruikbaar
|
||
- **Loki** en **Grafana Alloy** bewaren logs; Dozzle liet alleen zien wat er nú
|
||
gebeurt, en na een herstart was de oorzaak weg
|
||
- **Unbound** zoekt DNS zelf op. Pi-hole en AdGuard blokkeerden wel reclame, maar
|
||
stuurden de rest gewoon door naar Google of Cloudflare
|
||
|
||
**Domeinen die ontbraken:** evcc (zonnepanelen, thuisbatterij en laadpaal op
|
||
elkaar afstemmen), Healthchecks (merkt dat een cronjob níét liep — Uptime Kuma
|
||
merkt alleen dat iets uit ligt), Infisical (geheimen voor je applicaties, waar
|
||
Vaultwarden voor mensen is), Music Assistant en pgAdmin.
|
||
|
||
**Verder:** Crafty Controller, Manyfold, ErsatzTV, FitTrackee, ComfyUI, Omada
|
||
Controller, en de zwaardere Zabbix, Graylog, Wazuh, NetBox, Seafile en OpenCloud.
|
||
Van 218 naar 241 apps. Monitoring gaat van 18 naar 27.
|
||
|
||
**Een controle die zichzelf tegensprak.** De image-controle draaide met zestien
|
||
verzoeken tegelijk en meldde negen ontbrekende lscr.io-images. Alle negen bleken
|
||
te bestaan: registries knijpen af, en een afgeknepen verzoek ziet er precies zo
|
||
uit als een image dat er niet is. Bijna had ik negen werkende sjablonen
|
||
"gerepareerd". Die controle zit nu in `tools/controleer_images.py`, met vier
|
||
tegelijk en opnieuw proberen bij twijfel, en draait als stap in CI.
|
||
|
||
# v0.7.83-beta — Vervangers voor big tech, en sleutels in het juiste formaat
|
||
|
||
**Een sleutel is niet zomaar een willekeurige string.** De generator maakte
|
||
altijd url-veilige base64, en dat is precies wat Laravel, Homarr en LibreChat
|
||
níét accepteren. Laravel wil `base64:` ervoor met standaard-base64; Homarr wil 64
|
||
hexadecimale tekens; LibreChat wil er twee, van 64 en 32. Een waarde in het
|
||
verkeerde formaat levert een container op die niet start, met een foutmelding die
|
||
nergens naar de sleutel wijst. Sjablonen kunnen nu `secret_format` (`hex` of
|
||
`laravel`) en `secret_bytes` opgeven.
|
||
|
||
Daarmee vielen twee fouten uit de vorige versie op: **Homarr** en **LibreChat**
|
||
kregen sleutels die ze hadden geweigerd. Rechtgezet. En **Snipe-IT** en **Invoice
|
||
Ninja**, die vorige ronde afvielen omdat hun APP_KEY niet te genereren was, staan
|
||
er nu wel in.
|
||
|
||
**Negentien vervangers voor big tech.** De catalogus gaat van 197 naar 218 apps:
|
||
|
||
| In plaats van | Nu |
|
||
|---|---|
|
||
| Google Agenda en Contacten | Baïkal, Radicale |
|
||
| Google Translate | LibreTranslate |
|
||
| Google Docs | CryptPad, Collabora Online, ONLYOFFICE |
|
||
| Google Forms | Formbricks |
|
||
| Google Meet en Zoom | Jitsi Meet |
|
||
| YouTube | PeerTube, Invidious, Tube Archivist |
|
||
| Twitch | Owncast |
|
||
| X (Twitter) | Mastodon |
|
||
| Instagram | Pixelfed |
|
||
| Reddit | Lemmy |
|
||
| Calendly | Cal.com |
|
||
| Doodle | Rallly |
|
||
| DocuSign | DocuSeal |
|
||
| Figma | Penpot |
|
||
|
||
Communicatie gaat daarmee van 12 naar 16 apps en productiviteit van 21 naar 31 —
|
||
de twee hoeken waar zelfhosten het meest oplevert en de catalogus het dunst was.
|
||
|
||
**Wat er niet in ging.** Bij een paar apps staat een waarschuwing in de
|
||
omschrijving in plaats van dat ik doe alsof het vanzelf gaat: Mastodon en PeerTube
|
||
leggen hun domein vast zodra andere servers je kennen, CryptPad heeft echt twee
|
||
verschillende domeinen nodig om zijn sandbox te laten werken, en LibreTranslate
|
||
haalt bij de eerste start zijn taalmodellen op.
|
||
|
||
# v0.7.82-beta — 55 apps erbij, en drie kapotte images gevonden
|
||
|
||
**De catalogus stond scheef.** Na het ARR-werk telde media 42 apps en downloaden
|
||
19, terwijl communicatie er 4 had, AI 4, financiën 3 en foto's 2. Wie de store
|
||
opende voor iets anders dan media, vond het vaak niet. Er zijn nu 55 apps bij, van
|
||
142 naar 197, gekozen op wat er in 2026 daadwerkelijk gedraaid wordt.
|
||
|
||
**Drie nieuwe categorieën:** Statistiek (Umami, Matomo), Zakelijk (Odoo,
|
||
FreeScout, EspoCRM) en Spellen (RomM, Minecraft) — hoeken die er helemaal niet
|
||
waren.
|
||
|
||
**Homelab-gereedschap:** Dockge, Komodo, Glance, Gatus, Scrutiny, Backrest,
|
||
Headscale, Zoraxy, CrowdSec, NetAlertX, Pocket ID en Authentik.
|
||
**Communicatie** gaat van 4 naar 12 met Mattermost, Synapse, Element, Listmonk,
|
||
Mumble, Roundcube en Stalwart. **Notities en documenten:** Outline, Trilium,
|
||
SilverBullet, Readeck, Docmost en Kiwix. Verder LibreChat, AnythingLLM, LocalAI,
|
||
Ghostfolio, Wallos, Maybe, Lychee, Piwigo, Homebox, wger, Donetick, Dawarich,
|
||
Gitea, Adminer, code-server, Woodpecker, Semaphore, Opengist, MinIO, Pingvin
|
||
Share, Leantime, PrivateBin, Emby en ConvertX.
|
||
|
||
**Drie apps in de catalogus waren niet te installeren.** De controle die bij het
|
||
ARR-werk Huntarr ontmaskerde, is nu over alle 213 images in de catalogus gehaald.
|
||
Daaruit kwamen drie stille fouten: Forgejo wees naar `:latest` terwijl dat project
|
||
helemaal geen latest-tag publiceert, Planka naar een tag `1` die niet bestaat, en
|
||
Baby Buddy naar `ghcr.io/babybuddy/babybuddy` — een pad dat er niet is, terwijl
|
||
het sjabloon met zijn PUID/PGID al voor de linuxserver-variant geschreven was. Alle
|
||
drie rechtgezet.
|
||
|
||
**Twee testregels waren te krap.** Een `config.yaml` in `files/` die geen
|
||
compose-bestand is, viel tussen wal en schip: hij werd niet als compose gescand en
|
||
overgeslagen omdat hij op `.yaml` eindigt — velden die alleen dáár gebruikt werden,
|
||
gingen door voor ongebruikt. En een verplicht geheim dat de gebruiker ergens anders
|
||
vandaan haalt, stond in een handmatige lijst met veldnamen. Dat is nu een vlag in
|
||
het sjabloon zelf: `"eigen_invoer": true`.
|
||
|
||
**Nieuwe controles.** Zesendertig apps brengen inmiddels hun eigen database mee.
|
||
Daarvoor geldt nu: elke extra container draagt de servicenaam als voorvoegsel
|
||
(anders botsen twee installaties van dezelfde app), geen enkel databasewachtwoord
|
||
staat vast in een sjabloon, en er wacht altijd iemand met `depends_on` op de
|
||
database.
|
||
|
||
# v0.7.81-beta — De ARR-stack uit elkaar, en netwerkopties per container
|
||
|
||
**Elk *arr-onderdeel is nu een losse app.** Wie alleen Sonarr wil, hoefde eerst
|
||
de hele ARR-stack te nemen. Er staan nu zesenveertig losse sjablonen in de store
|
||
— Sonarr, Radarr, Prowlarr, Bazarr, Gluetun, en alles daaromheen — en de
|
||
gecombineerde stack blijft bestaan als "alles in één keer". Het zijn dezelfde
|
||
containers: beide verpakkingen komen uit één bron (`tools/arr_sjablonen.py`) en
|
||
een test draait die opnieuw en vergelijkt het resultaat, zodat ze niet stilletjes
|
||
uit elkaar groeien. Die test vond meteen zijn eerste geval: los koppelde
|
||
qBittorrent zijn interne poort aan de hostpoort die jij koos, wat niet meer klopt
|
||
zodra hij achter een VPN zit.
|
||
|
||
**Hardlinks werkten niet, en dat was niet zichtbaar.** Elke app kreeg losse
|
||
mounts — `/tv`, `/movies`, `/downloads`. Voor Sonarr zijn dat twee verschillende
|
||
bestandssystemen, ook al staat alles op dezelfde schijf, dus werd élke voltooide
|
||
download naar je bibliotheek *gekopieerd* in plaats van gelinkt: traag, tijdelijk
|
||
alles dubbel, en je torrent stopte met seeden zodra je opruimde. De beschrijving
|
||
waarschuwde wel voor "zet ze op hetzelfde bestandssysteem", maar de mounts maakten
|
||
het alsnog onmogelijk. Alles deelt nu één map die als `/data` binnenkomt, met
|
||
`UMASK=002` zodat de apps bij elkaars bestanden kunnen.
|
||
|
||
**Negentien lege kaarten.** De schakelaar van een onderdeel stond hard in de stap
|
||
*Instellingen*, terwijl je hem ook in de kop van zijn eigen groep in *Basis* zag
|
||
staan: elk onderdeel had twee schakelaars in twee schermen, en *Instellingen*
|
||
bestond voor het grootste deel uit kaarten zonder inhoud. Er is nu een eerste
|
||
stap **Onderdelen** waar alle schakelaars bij elkaar staan, gegroepeerd per soort
|
||
— bij negenenveertig onderdelen scheelt dat nogal. Wat je uitzet verdwijnt uit de
|
||
volgende stappen in plaats van als lege kaart te blijven staan.
|
||
|
||
**Een eigen IP-adres ging per stack, niet per container.** Bij een app met één
|
||
container klopt dat. Bij een stack met twintig webinterfaces kreeg de eerste
|
||
container het adres en raakten **alle andere hun poortmappings kwijt** zonder er
|
||
iets voor terug te krijgen — daarna waren ze nergens meer te bereiken. Je wijst
|
||
nu per container een adres toe; wie er geen krijgt, houdt gewoon zijn poort.
|
||
Hetzelfde gold voor Pangolin, dat één URL per stack maakte op de eerste poort die
|
||
het tegenkwam: dat gaat nu ook per container.
|
||
|
||
**Huntarr is eruit.** Het project werd in februari 2026 offline gehaald nadat er
|
||
onder meer authenticatie-bypasses en API-sleutels in platte tekst waren gevonden;
|
||
de auteur heeft de repo verwijderd. Het image dat in het sjabloon stond bestaat
|
||
niet meer — een controle langs alle registries bevestigde dat. Vervangen door
|
||
NeutArr, de fork van de laatste schone versie. In diezelfde ronde bleek
|
||
Maintainerr verhuisd naar een andere organisatie.
|
||
|
||
**Nieuw in de catalogus:** Overseerr, Cleanuparr, Decluttarr, Janitorr,
|
||
cross-seed, Configarr, Profilarr, Byparr, Kometa, Tautulli, Jellystat, Wizarr,
|
||
SuggestArr, Trailarr, Lingarr, NZBHydra2, Deluge, Transmission, slskd, Soularr,
|
||
Mylar3, Kapowarr, Pulsarr, Prefetcharr, Posterizarr, Notifiarr, Exportarr,
|
||
Autopulse, Requestrr, Homarr en NeutArr. Alle images zijn tegen hun registry
|
||
gecontroleerd.
|
||
|
||
**Twee mappen die allebei "data" heetten.** `data_dir` was de map waar een app
|
||
zijn eigen spullen bewaart; daar kwam nu een gedeelde `/data` naast. Die heten nu
|
||
`appdata_dir` en `data_root`, in alle sjablonen. En qBittorrent stond standaard op
|
||
poort 8080, samen met zes andere apps; die staat nu op 8097.
|
||
|
||
# v0.7.80-beta — De knop Volgende, tijdzones, en netwerken per groepje
|
||
|
||
**De knop Volgende deed niets.** Alpine haalt een boolean-attribuut alleen weg
|
||
bij `null`, `undefined` of `false` — het getal `0` zét het juist. Mijn
|
||
`:disabled="stepMissing().length"` gaf bij een compleet ingevuld formulier `0`,
|
||
en daarmee stond de knop permanent uit. De logica klopte, de binding niet.
|
||
|
||
Dat was met losse logica niet te vinden, dus er draait nu een test die de echte
|
||
modal met Alpine in een DOM rendert en er daadwerkelijk op klikt. Die vangt
|
||
precies dit soort bindingsfouten. Hij slaat zichzelf over waar `jsdom` ontbreekt.
|
||
|
||
**Tijdzones komen van de server.** In alle 77 sjablonen die er een hebben stond
|
||
`Europe/Amsterdam` hard ingevuld — dat klopt alleen toevallig. Er is nu een
|
||
instelling **Tijdzone** die standaard de tijdzone van je server overneemt (uit
|
||
`TZ`, `/etc/timezone` of `/etc/localtime`), en die wordt als voorstel gebruikt.
|
||
De negentien apps zonder tijdzoneveld krijgen `TZ` er automatisch bij, want die
|
||
draaiden tot nu toe in UTC en zetten dus overal het verkeerde tijdstip neer.
|
||
|
||
**Poortcontrole kijkt nu ook naar de host.** `docker ps` toont alleen wat Docker
|
||
publiceert; een nginx of DNS-server die rechtstreeks op de host draait bleef
|
||
onzichtbaar terwijl je er net zo goed mee botst. Die worden nu meegeteld, via
|
||
`/proc/net/tcp` in een hulpcontainer met `--network host`. Lukt dat niet, dan
|
||
zegt het antwoord `host_checked: false` — "vrij" is dan een aanname.
|
||
|
||
**Niet alles hoeft meer aan alles vast.** Er was één gedeeld netwerk waar élke
|
||
app aan hing. Je kiest nu per app aan welke netwerken hij meedoet, en kunt er bij
|
||
het installeren zelf een maken — met een eigen naam of een voorgestelde
|
||
(`su-<appnaam>`). Dat netwerk verschijnt daarna bij elke andere app in de lijst.
|
||
Een app kan aan meerdere netwerken tegelijk hangen.
|
||
|
||
**Advies bij IP-adressen.** Vul je een adres in, dan wordt gecontroleerd of het
|
||
binnen het subnet valt, binnen je ingestelde bereik, of het al aan een container
|
||
is toegekend, en of de host het recent op het LAN heeft gezien — dat laatste uit
|
||
de ARP-tabel, en dat vangt een fysiek apparaat met een DHCP-adres. Klopt er iets
|
||
niet, dan staat erbij wát en krijg je een vrij adres voorgesteld.
|
||
|
||
**Nieuw: Pangolin.** Onder Inloggegevens kun je met één klik een publiek adres
|
||
voor een app aanmaken via een [Pangolin](https://github.com/fosrl/pangolin)-tunnel,
|
||
zonder poorten open te zetten. Zie [`docs/pangolin.md`](docs/pangolin.md).
|
||
|
||
> Deze integratie is getest tegen een nagebouwde API, niet tegen een echte
|
||
> Pangolin-server — die heb ik hier niet. De API veranderde bovendien in
|
||
> versie 1.9 (een resource hing eerst onder een site en staat nu los), dus
|
||
> beide routes worden geprobeerd en bij een fout krijg je beide meldingen te
|
||
> zien.
|
||
|
||
2369 tests groen.
|
||
|
||
---
|
||
|
||
# v0.7.70-beta — Installeren via een invulmenu, geheimen naar .env
|
||
|
||
**Het installatieformulier is een invulmenu geworden.** Eén lange lijst werkte
|
||
slecht aan beide uiteinden: de mediane app heeft vijf velden in één groep, en de
|
||
ARR-stack tweeënvijftig in eenentwintig groepen. Nu zijn er vaste stappen —
|
||
Basis, Verbinden, Instellingen, Toegang, Controleren — waarbij een lege stap
|
||
wordt overgeslagen. Een eenvoudige app krijgt er dus drie, de ARR-stack vijf.
|
||
|
||
**Volgende** controleert de verplichte velden van díé stap en noemt wat er nog
|
||
leeg is. Tot nu toe kwam die melding pas als je op Installeren drukte. De
|
||
stapindicator is klikbaar om terug te springen.
|
||
|
||
**Geavanceerde opties zitten achter één schakelaar** die op elke stap zichtbaar
|
||
is, in plaats van een uitklapblok per groep dat je steeds opnieuw moest zoeken.
|
||
Een badge toont hoeveel velden verborgen zijn. Er is ook een schakelaar **Alles
|
||
op één pagina** voor wie het oude gedrag wil; beide keuzes worden onthouden.
|
||
|
||
**Geheimen gaan naar `.env`.** Wachtwoorden en sleutels staan niet meer in het
|
||
compose-bestand maar als `${NAAM}` met de waarde in `.env`, geschreven met
|
||
rechten `0600`. Poorten, paden en tijdzone blijven leesbaar in compose, zodat je
|
||
nog steeds in één bestand kunt zien wat er draait. Ook `.serverup.json` bevat
|
||
geen geheimen meer en krijgt `0600` — daar stonden ze tot nu toe wereldleesbaar
|
||
in.
|
||
|
||
Eerlijk over de grens hiervan: compose vult `${...}` in bij het inlezen, dus
|
||
`docker inspect` toont de waarde nog steeds. Wat je wint is dat het
|
||
compose-bestand deelbaar wordt en dat alles op één afgeschermde plek staat.
|
||
`docs/beveiliging.md` legt uit wanneer je verder wilt gaan met `secrets:` van
|
||
Compose, en waarom een externe kluis hier een slecht idee is.
|
||
|
||
**Compose en `.env` zijn nu ook vóór het installeren te bewerken.** De laatste
|
||
stap toont het gerenderde resultaat met de waarden die je hebt ingevuld; met
|
||
Bewerken pas je het aan en gaat jouw versie mee in plaats van het sjabloon.
|
||
Ongeldige YAML wordt geweigerd voordat er een map is aangemaakt. De editor voor
|
||
bestaande stacks heeft tabbladen gekregen (je moest hem sluiten en opnieuw
|
||
openen om van compose naar `.env` te gaan) en biedt aan te herstarten na
|
||
opslaan. De `.env`-editor weigert nu een regel zonder `=` — compose negeert die
|
||
stilzwijgend, waarna er ineens een wachtwoord mist — en schrijft een auditregel.
|
||
|
||
**Nieuw: Inloggegevens per app.** Een knop op de stackkaart toont het adres, de
|
||
gebruikersnaam en het wachtwoord, met een oog-knop en een kopieerknop. Bij een
|
||
gegenereerd wachtwoord kon je dat nergens meer terugvinden. Apps die je bij het
|
||
eerste bezoek zelf een account laten aanmaken (Vaultwarden, Immich) melden dat.
|
||
Alleen beheerders kunnen erbij en elk bekijken komt in het auditlog.
|
||
|
||
**Herstel van een regressie uit v0.7.60.** Ik had de genereerknop toen aan
|
||
`generate` gehangen in plaats van aan `secret`, waardoor vijftien velden hun knop
|
||
kwijtraakten. Acht daarvan zijn inlogwachtwoorden (InfluxDB, Kimai, Linkding,
|
||
Miniflux, PhotoPrism, Pi-hole, Technitium, ChangeDetection) en die hebben hem
|
||
terug. De zeven waar een verzonnen waarde juist fout is — een WireGuard-
|
||
privésleutel, een Beszel-agentsleutel, tokens uit een andere app — houden geen
|
||
knop.
|
||
|
||
Bij het uitzoeken bleek ook dat ik in v0.7.60 twee dingen verkeerd heb gemeld.
|
||
De genereerknop wérkte destijds wel (de vlag werd gezet in `app.py`, niet in
|
||
`boilerplates.py` waar ik keek), en er wás al een compose- en `.env`-editor.
|
||
|
||
De logica van het invulmenu wordt nu getest door de echte component uit
|
||
`index.html` in Node uit te voeren; die test slaat zichzelf over waar Node
|
||
ontbreekt, zoals in het bouwimage. 2295 tests groen.
|
||
|
||
---
|
||
|
||
# v0.7.60-beta — App-instellingen die niet meer stilzwijgend misgaan
|
||
|
||
Aanleiding: Homepage weigerde na installatie met **"Host validation failed"**.
|
||
De standaardwaarde van `allowed_hosts` noemde poort 3001 terwijl het poortveld
|
||
op 3002 stond — en `localhost` werkt sowieso niet als je je server op zijn
|
||
IP-adres benadert. Dat bleek geen los geval, dus alle 96 sjablonen zijn
|
||
nagelopen.
|
||
|
||
**De dobbelsteenknop bestond niet.** Zeventien apps zeggen in hun uitleg
|
||
"gebruik de dobbelsteenknop voor een willekeurige waarde". Die knop hing af van
|
||
een veld `secret` dat de backend nooit meestuurde, dus hij verscheen nooit — en
|
||
wachtwoorden stonden als gewone leesbare tekst in het formulier. Nu worden
|
||
geheimen herkend, en 27 velden die je toch nooit zelf typt (databasewachtwoorden,
|
||
JWT- en versleutelingssleutels) worden meteen ingevuld met een willekeurige
|
||
waarde. Inlogwachtwoorden krijgen alleen de knop: die moet je zelf noteren.
|
||
|
||
De knop hangt nu aan "hier past een willekeurige waarde" en niet aan "dit is
|
||
geheim" — voor een WireGuard-privésleutel of een token uit een andere app is
|
||
iets verzonnens juist fout.
|
||
|
||
**Verplichte velden werden niet gecontroleerd.** Het formulier zette een
|
||
sterretje achter een verplicht veld en hield verder niets tegen: je kon een app
|
||
installeren met een leeg databasewachtwoord of een lege sleutel. De container
|
||
start dan niet, of draait met een leeg geheim. Dat wordt nu geweigerd, bij
|
||
installeren én bij het achteraf wijzigen van instellingen, met de melding welke
|
||
velden nog leeg zijn. Velden achter een uitgeschakelde groepsschakelaar tellen
|
||
niet mee.
|
||
|
||
**Acht apps hadden een vast wachtwoord in het sjabloon**: `immich_db_pass`,
|
||
`ghost_db_pass` en soortgelijke voor iedereen die het installeert, en `admin` als
|
||
beheerderswachtwoord van Grafana en Gotify. De databasewachtwoorden worden nu
|
||
gegenereerd; bij Grafana en Gotify moet je zelf een wachtwoord kiezen.
|
||
|
||
**Wachtwoorden stonden in het installatielog**, dat in de interface zichtbaar is
|
||
en vaak in een bugmelding geplakt wordt. Die worden nu gemaskeerd.
|
||
|
||
Verder rechtgezet:
|
||
|
||
- `baserow`, `hedgedoc` en `ntfy` hadden dezelfde poortfout als Homepage: de
|
||
URL in de standaardwaarde wees naar een poort waar niets luistert, waardoor
|
||
links en terugkeer-na-inloggen niet werkten.
|
||
- Zes velden stonden in het formulier maar kwamen nergens terecht: `data_dir`
|
||
bij Keycloak, MeTube, Miniflux en TeslaMate (die gebruiken een Docker-volume,
|
||
dus je gegevens stonden niet waar je dacht), en `puid`/`pgid` bij FreshRSS,
|
||
die het image helemaal niet kent.
|
||
- De tijdzone van Prometheus werd wel gevraagd maar niet doorgegeven.
|
||
|
||
Nieuw is `tests/test_app_templates.py`: elke app wordt met zijn
|
||
standaardwaarden gerenderd en gecontroleerd op ongedefinieerde variabelen,
|
||
velden zonder werking, niet-gedeclareerde volumes, `depends_on` naar een
|
||
onbekende service, dubbele containernamen, poortconflicten binnen een stack,
|
||
poortnummers in standaardwaarden die niet kloppen met het poortveld, en vaste
|
||
wachtwoorden. 2236 tests groen.
|
||
|
||
---
|
||
|
||
# v0.7.50-beta — Je hoort het als er iets misgaat
|
||
|
||
Het onbewaakte deel was de zwakke plek. Geplande backups, updatechecks en de
|
||
zelf-update draaien 's nachts zonder toezicht; ging er iets mis, dan kwam dat
|
||
alleen in het auditlog terecht. Een backup die drie weken stilletjes faalt,
|
||
ontdek je op het slechtst denkbare moment.
|
||
|
||
**Meldingen** via ntfy, een webhook of e-mail — alle drie met wat er al in
|
||
Server Up zat, dus geen extra software. Je krijgt bericht bij een mislukte
|
||
backup, een archief dat niet leesbaar blijkt, een vastgelopen geplande taak,
|
||
weinig schijfruimte, een beschikbare update en een container die zou moeten
|
||
draaien maar dat niet doet. Standaard staan alleen de storingen aan.
|
||
|
||
Eén bericht per ronde, niet één per stack: bij een volle schijf faalt alles
|
||
tegelijk en dan wil je geen twintig meldingen. Bij weinig ruimte en gestopte
|
||
containers wordt alleen de overgang gemeld, anders krijg je elk uur hetzelfde.
|
||
Er is een testknop die eerst je instellingen opslaat, zodat je test wat je net
|
||
hebt ingevuld. Een storing in het meldingskanaal kan de taak die de melding
|
||
veroorzaakte nooit alsnog laten omvallen.
|
||
|
||
**Schijfruimte** wordt nu bewaakt. Vóór elke backup wordt gekeken of het er
|
||
redelijkerwijs in past; zo niet, dan wordt hij geweigerd in plaats van
|
||
halverwege afgebroken — een half archief ziet er in een lijst compleet uit.
|
||
Daarbovenop blijft `DISK_MIN_FREE_GB` gereserveerd, want een volle schijf op een
|
||
Docker-host breekt alles en niet alleen Server Up. In de backuplijst staat per
|
||
schijf een balkje, dat rood kleurt onder `DISK_WARN_PCT`.
|
||
|
||
**Backups worden gecontroleerd.** Elk nieuw archief wordt meteen helemaal
|
||
uitgelezen: dat controleert de tar-structuur én de gzip-checksum. Die checksum
|
||
staat aan het eind, dus een archief dat halverwege is afgebroken valt door de
|
||
mand terwijl het in een directorylisting compleet lijkt. Er is ook een diepe
|
||
variant die echt uitpakt naar een tijdelijke map, met dezelfde beperkingen als
|
||
een echt herstel — de enige manier om te weten dat terugzetten werkt vóórdat je
|
||
het nodig hebt. Achter elk archief staat een schildje: groen als het gelezen is,
|
||
rood met de foutmelding als het beschadigd is.
|
||
|
||
**Backups kunnen de deur uit.** Er is een downloadknop, en met
|
||
`BACKUP_OFFSITE_DIR` wordt elke nieuwe backup gekopieerd naar een tweede
|
||
bestemming: een gemounte schijf, NFS- of SMB-share. Tot nu toe stonden backups
|
||
uitsluitend op dezelfde schijf als de data die ze moeten beschermen, en kon je
|
||
ze er niet eens afhalen. Het kopiëren gaat via een `.part`-bestand dat daarna
|
||
hernoemd wordt, en valt de mount weg — dan bestaat het pad vaak nog als lege map
|
||
op je systeemschijf — dan wordt eerst gekeken of er ruimte is, zodat je niet
|
||
ongemerkt de verkeerde schijf vult.
|
||
|
||
Het token en het SMTP-wachtwoord zijn write-only, net als de git- en
|
||
registry-tokens: instellen kan, teruglezen niet.
|
||
|
||
Nieuw: [`docs/meldingen.md`](docs/meldingen.md), en `docs/backups.md` is
|
||
uitgebreid met controleren, schijfruimte, de tweede bestemming en downloaden.
|
||
|
||
Getest tegen een draaiende server met een echte ontvanger voor de meldingen:
|
||
een gehalveerd archief wordt herkend, een volle schijf weigert de backup zonder
|
||
iets achter te laten, het gedownloade bestand is een uitpakbare tarball, en een
|
||
padtruc in de bestandsnaam levert een 400 op. 1064 tests groen.
|
||
|
||
---
|
||
|
||
# v0.7.40-beta — Account en bereikbaarheid meteen goed
|
||
|
||
Na het installeren was je nog niet klaar: je moest zelf naar de webinterface om
|
||
een beheerdersaccount te claimen, en stond `BIND` op `127.0.0.1`, dan kón je daar
|
||
niet eens bij zonder eerst `.env` aan te passen. Het script regelt nu allebei.
|
||
|
||
**Beheerdersaccount.** Op een terminal vraagt het script of je er meteen één wil,
|
||
en zo ja om een naam en wachtwoord (twee keer, met de echo uit). Automatisch gaat
|
||
het met `--admin NAAM` plus `--admin-password-file PAD` of `SU_ADMIN_PASSWORD`.
|
||
Is er geen wachtwoordbron én geen terminal, dan maakt het script er zelf een van
|
||
24 tekens en zet die op het scherm.
|
||
|
||
Het wachtwoord kan bewust *niet* als optie mee: `--admin-password` weigert met
|
||
uitleg. Opdrachtregelargumenten zijn voor elke gebruiker op de server zichtbaar
|
||
met `ps` en blijven in je shell-geschiedenis staan. Onderweg naar de server gaat
|
||
het via stdin naar `curl`, niet als argument.
|
||
|
||
Nieuw is ook **`--create-admin`**: alleen het account aanmaken, bij een
|
||
installatie die al draait. Voor als het de eerste keer is misgegaan of je het
|
||
hebt overgeslagen. De poort komt daarbij uit `.env`.
|
||
|
||
**Bereikbaarheid.** Het script vraagt nu waarop de interface moet luisteren:
|
||
alleen deze server, het hele netwerk (met het gedetecteerde adres erbij), of een
|
||
adres dat je zelf opgeeft. Installeer je opnieuw over een bestaande map, dan is
|
||
je huidige instelling het uitgangspunt en houdt enter die vast.
|
||
|
||
Daarbij zat een fout die je zelf tegenkwam: een bestaande `.env` bleef *altijd*
|
||
ongemoeid, ook als je `--bind` meegaf. De installatie leek dan te lukken terwijl
|
||
de server op het oude adres bleef luisteren. Nu wordt doorgevoerd wat je
|
||
expliciet meegeeft, en blijft de rest staan. Andersom rekent het script bij
|
||
`--update` voortaan met wat er écht in `.env` staat: draaide je op poort 8080,
|
||
dan wachtte het daarvoor op poort 5000 en meldde het het verkeerde adres.
|
||
|
||
Verder: `--dry-run` riep `sudo docker` aan en vroeg dus om een wachtwoord,
|
||
terwijl het net beloofd had niets te doen — zonder terminal liep het daar vast.
|
||
Een proefdraai spreekt de Docker-daemon nu niet meer aan. En een ongeldig
|
||
bind-adres of een poort buiten 1–65535 wordt meteen geweigerd in plaats van als
|
||
een onbegrijpelijke fout uit `docker compose` terug te komen.
|
||
|
||
De vragen worden allemaal vooraf gesteld, zodat je kunt weglopen terwijl het
|
||
image gebouwd wordt.
|
||
|
||
Getest met een echte pseudo-terminal voor de vragen, en tegen een echt
|
||
draaiende server voor het aanmaken van het account — inclusief een wachtwoord
|
||
vol aanhalingstekens en backslashes, een tweede account dat geweigerd hoort te
|
||
worden, en een server die niet reageert.
|
||
|
||
---
|
||
|
||
# v0.7.30 — Installeren met één regel
|
||
|
||
```bash
|
||
curl -fsSL https://git.ramonbesselink.nl/bes-r/server-up/raw/branch/main/install.sh | sh
|
||
```
|
||
|
||
Het script controleert je systeem, biedt aan Docker te installeren als dat
|
||
ontbreekt, haalt de code op, maakt `.env` aan, bouwt het image, start de
|
||
container en wacht tot `/healthz` antwoordt. Daarna toont het waar je terecht
|
||
kunt — met de waarschuwing dat je meteen een beheerdersaccount moet aanmaken.
|
||
|
||
Opties: `--dir`, `--port`, `--bind`, `--branch`, `--token`, `--update`,
|
||
`--uninstall`, `--yes` en `--dry-run`.
|
||
|
||
**`--dry-run`** toont de hele gang van zaken zonder iets te wijzigen. Handig om
|
||
te zien wat er gaat gebeuren voordat je een script met root-rechten loslaat op
|
||
je server; `docs/installeren.md` zet die stap dan ook vooraan.
|
||
|
||
**Bijwerken** gaat met `sh /opt/server-up/install.sh --update`: nieuwe code
|
||
ophalen, opnieuw bouwen, herstarten. Je `.env`, stacks en gegevens blijven staan.
|
||
**Verwijderen** stopt de container maar laat je gegevens met rust, en vertelt
|
||
daarna hoe je die alsnog opruimt.
|
||
|
||
Het script gebruikt alleen `sudo` waar dat nodig is: kun je zelf al in de
|
||
doelmap schrijven én docker aanroepen, dan blijft alles onder je eigen account.
|
||
|
||
Nieuw: `README.md` verwijst er nu naar, plus `docs/installeren.md` met alle
|
||
opties en een probleemoplostabel.
|
||
|
||
Getest onder `sh`, `dash` en `bash`, met een testsuite die controleert dat er
|
||
geen bashismen in sluipen, dat een typefout in de opties netjes stopt, en dat
|
||
een proefdraai echt niets aanmaakt. Twee dingen die daarbij naar boven kwamen:
|
||
de wachtlus gaf anderhalve minuut lang geen enkel teken van leven (nu puntjes),
|
||
en `docker compose --project-directory` zoekt zonder `-f` het compose-bestand
|
||
alsnog in de huidige map — wat bij `curl | sh` je thuismap is.
|
||
|
||
Een derde kwam pas op de bouwserver boven: ontbrak `curl`, dan stopte ook een
|
||
proefdraai meteen. Juist dan wil je zien wat er gaat gebeuren, dus dat is nu een
|
||
waarschuwing in plaats van een harde fout — net als bij een ontbrekende Docker.
|
||
|
||
Dit is de eerste stabiele release sinds v0.4.60. Alles uit de v0.5-, v0.6- en
|
||
v0.7-beta's zit erin: inloggen met rollen, backups, netwerken met een eigen
|
||
IP-adres, 96 apps met categorieën, apps onderling koppelen, instellingen
|
||
achteraf wijzigen, en zelf bijwerken. Draaide je nog v0.4.60, lees dan eerst
|
||
`docs/beveiliging.md`: daar stond de interface nog open zonder login, en na het
|
||
bijwerken moet je meteen een beheerdersaccount aanmaken.
|
||
|
||
---
|
||
|
||
# v0.7.20-beta — Instellingen wijzigen, koppelen achteraf, en een defect uit v0.7.00
|
||
|
||
## 🔴 Gedeeld netwerk botste met `network_mode`
|
||
|
||
De koppeling uit v0.7.00 hing élke service aan het gedeelde netwerk. Maar een
|
||
service met `network_mode` deelt al andermans namespace, en docker compose
|
||
weigert die combinatie. Met de standaardkeuze "Verbinden met andere apps" waren
|
||
**Homebridge**, **Scrypted**, **Beszel** en de **ARR-stack met Gluetun** daardoor
|
||
niet te installeren.
|
||
|
||
Services met `network_mode` worden nu overgeslagen. Een test past het gedeelde
|
||
netwerk toe over alle 96 apps en controleert dat die combinatie nooit ontstaat.
|
||
|
||
## ⚙️ Instellingen wijzigen na installatie
|
||
|
||
Je koos bij het installeren netjes een poort, map en wachtwoord — maar daarna was
|
||
er geen weg terug naar dat formulier. Voor een andere poort moest je het
|
||
compose-bestand met de hand bewerken.
|
||
|
||
De knop **Instellingen** op elke stackkaart opent hetzelfde formulier, voorgevuld
|
||
met wat je destijds hebt gekozen. Opslaan maakt eerst een backup, rendert de
|
||
stack opnieuw, valideert en herstart. Loopt het valideren mis, dan wordt de
|
||
backup automatisch teruggezet.
|
||
|
||
Stacks die zijn geïnstalleerd voordat Server Up die keuzes bewaarde, hebben de
|
||
knop uit staan met uitleg waarom.
|
||
|
||
## 🔗 Bestaande stacks alsnog koppelen
|
||
|
||
Het gedeelde netwerk gold alleen voor nieuwe installaties. Een knop op de
|
||
stackkaart zet een bestaande stack er alsnog op, valideert en herstart. Wordt de
|
||
compose ongeldig, dan wordt de wijziging teruggedraaid.
|
||
|
||
## 🔍 Audit-log doorzoekbaar
|
||
|
||
Het log toonde de laatste tweehonderd regels zonder filter, terwijl er inmiddels
|
||
logins, rolwijzigingen, backups en updates in landen. Nu filteren op bron, actie,
|
||
status en periode, met vrij zoeken door referentie, details en IP-adres, en
|
||
doorbladeren voorbij de eerste honderd.
|
||
|
||
## 📖 Documentatie
|
||
|
||
- **`README.md`** — die was er niet. Wat Server Up is, installeren, het eerste
|
||
account, en waar je verder moet kijken.
|
||
- **`docs/apps-maken.md`** — het volledige templateformaat: velden, geavanceerde
|
||
opties, uitleg per veld, apps aan elkaar koppelen, onderdelen optioneel maken
|
||
met groepsschakelaars, en de valkuilen (named volumes, poorten, `network_mode`).
|
||
- **`docs/README.md`** — index over de zeven documenten.
|
||
|
||
De changelog-secties voor `v0.5.44`, `v0.5.45` en `v0.5.46` ontbraken; omdat
|
||
`release.yml` de notes daaruit haalt, zou een tag op die versies een lege release
|
||
opleveren. Aangevuld, met een test die afdwingt dat het huidige `VERSION` altijd
|
||
een sectie heeft.
|
||
|
||
---
|
||
|
||
# v0.7.10-beta — Categorieën met kleur, in de app store en bij je apps
|
||
|
||
Alle 96 apps zijn ingedeeld in zeventien categorieën, elk met een eigen kleur en
|
||
pictogram. Een app mag in meerdere categorieën zitten: Frigate staat onder
|
||
Domotica én Media, Immich onder Foto's én Opslag, Kavita onder Media én
|
||
Documenten. Negenendertig apps vallen onder meer dan één.
|
||
|
||
| | Categorie | | Categorie |
|
||
|---|---|---|---|
|
||
| 🟣 | Media | 🔵 | Netwerk |
|
||
| 🩷 | Foto's | 🔴 | Beveiliging |
|
||
| 🟢 | Downloaden | 🟠 | Opslag & backup |
|
||
| 🟩 | Domotica | 🩵 | Documenten |
|
||
| 🟦 | 3D-printen | 🟦 | Productiviteit |
|
||
| 🔷 | Communicatie | 🟩 | Financiën |
|
||
| 🌸 | Gezin & huishouden | 🟧 | Monitoring |
|
||
| 🩵 | Ontwikkeling | 🟪 | AI |
|
||
| ⬜ | Systeembeheer | | |
|
||
|
||
**In de app store** staat een gekleurde categoriebalk met per categorie het
|
||
aantal apps. Klikken filtert; het bestaande zoekveld en de tagfilters werken
|
||
ernaast gewoon door. Elke app-kaart toont zijn categorieën als gekleurde badge,
|
||
en klikken op zo'n badge filtert er meteen op.
|
||
|
||
**Bij je geïnstalleerde apps** staat dezelfde balk, met alleen de categorieën die
|
||
je daadwerkelijk draait. Elke stackkaart heeft een gekleurde rand in de kleur van
|
||
zijn eerste categorie, plus dezelfde klikbare badges.
|
||
|
||
Apps uit externe repo's (zoals de ChristianLempa-boilerplates) geven geen
|
||
categorieën op. Die worden afgeleid uit hun tags en omschrijving, zodat ook zij
|
||
een kleur en een plek in het filter krijgen in plaats van als naamloze groep
|
||
buiten de indeling te vallen.
|
||
|
||
Tests bewaken dat elke app minstens één geldige categorie heeft, dat de kleuren
|
||
onderling verschillen, en dat er geen categorie in het filter staat waar geen
|
||
enkele app onder valt.
|
||
|
||
---
|
||
|
||
# v0.7.00-beta — Apps koppelen, eenvoudiger instellen, geavanceerde opties verborgen
|
||
|
||
## 🔗 Apps kunnen elkaar nu bereiken
|
||
|
||
Elke stack draaide als eigen compose-project en kreeg daarmee zijn eigen
|
||
netwerk. Twee stacks konden elkaar dus niet op naam vinden: Zigbee2MQTT zag de
|
||
Mosquitto-broker uit een andere stack simpelweg niet.
|
||
|
||
Bij het installeren staat nu **"Verbinden met andere apps"** aan. De stack komt
|
||
dan op een gedeeld bridge-netwerk (`serverup`), waar containers elkaar op naam
|
||
oplossen. Het netwerk wordt aangemaakt zodra de eerste stack het nodig heeft.
|
||
|
||
## 🎯 Kiezen in plaats van typen
|
||
|
||
Een veld dat naar een andere app verwijst, toont voortaan een **keuzelijst met
|
||
wat je al hebt draaien** in plaats van een leeg tekstveld. Installeer je
|
||
Zigbee2MQTT en heb je Mosquitto al staan, dan staat `mqtt://mosquitto:1883` er
|
||
meteen ingevuld. Hetzelfde voor Open WebUI dat je Ollama vindt.
|
||
|
||
Staat de app er nog niet, dan zegt het veld dat — met de mogelijkheid om alsnog
|
||
handmatig een adres in te vullen.
|
||
|
||
## 💬 Uitleg per instelling
|
||
|
||
Achter elk veld met toelichting staat een **info-icoon**. Klikken opent een blok
|
||
met de volledige uitleg en de standaardwaarde. Voor de instellingen die de
|
||
meeste vragen oproepen is die uitleg uitgeschreven — waarom je downloadmap op
|
||
hetzelfde bestandssysteem moet staan als je mediamap, wat `shm_size` bij Frigate
|
||
doet, waarom Caddy poort 80 nodig heeft ook als je alles via HTTPS aanbiedt.
|
||
|
||
## 🎛️ Geavanceerde opties uit het zicht
|
||
|
||
Velden die je meestal met rust laat — database-wachtwoorden, PUID/PGID,
|
||
USB-paden, bewaartermijnen — zitten nu achter een uitklapblok **"Geavanceerde
|
||
opties"** met een teller erbij. Het installatieformulier begint daardoor met
|
||
alleen de vragen die er echt toe doen.
|
||
|
||
Templates kunnen `advanced: true` op een veld of op een hele groep zetten. Een
|
||
test bewaakt dat er per app altijd zichtbare velden overblijven, zodat niemand
|
||
per ongeluk een volledig leeg formulier maakt.
|
||
|
||
---
|
||
|
||
# v0.6.20-beta — 31 apps erbij na een rondgang langs de verzamelsites
|
||
|
||
Van 65 naar 96 apps. Selectie op basis van awesome-selfhosted, selfh.st,
|
||
Perfect Media Server en een paar overzichtslijsten van 2026, aangevuld met wat
|
||
er in de bestaande collectie nog ontbrak.
|
||
|
||
**Monitoring** — Prometheus (de tegenhanger van Grafana, die er al stond),
|
||
InfluxDB, Netdata, Beszel
|
||
|
||
**Reverse proxy** — Caddy, dat zijn certificaten volledig zelf regelt
|
||
|
||
**Notities en kennis** — Memos, HedgeDoc, Wiki.js, Excalidraw, Karakeep
|
||
|
||
**Data en dashboards** — NocoDB, Baserow, Metabase
|
||
|
||
**Identiteit** — Authelia en Keycloak, allebei bruikbaar met de
|
||
reverse-proxy-SSO van Server Up zelf
|
||
|
||
**Media** — Komga, PhotoPrism, MeTube, Pinchflat
|
||
|
||
**Meldingen** — ntfy
|
||
|
||
**Productiviteit** — Planka, Focalboard, Kimai
|
||
|
||
**Locatie en voertuig** — OwnTracks, TeslaMate
|
||
|
||
**Hulpmiddelen** — Shlink, CyberChef, LanguageTool, Kopia
|
||
|
||
**AI** — Ollama en Open WebUI, allebei volledig lokaal
|
||
|
||
De images van de nieuwere apps (Beszel, Karakeep, Pinchflat) zijn nagetrokken
|
||
bij de bron in plaats van uit het hoofd opgeschreven; van Karakeep is de
|
||
officiële compose overgenomen, inclusief de meilisearch- en chrome-containers
|
||
die hij nodig heeft.
|
||
|
||
Vijftien poortbotsingen met bestaande apps zijn automatisch rechtgezet. Caddy
|
||
deelt 80 en 443 met Traefik en Nginx Proxy Manager — alternatieven voor dezelfde
|
||
taak, dus dat staat in de allowlist.
|
||
|
||
---
|
||
|
||
# v0.6.10-beta — ARR-stack met een schakelaar per onderdeel
|
||
|
||
De ARR-stack installeerde altijd dezelfde zeven containers. Nu kies je per
|
||
onderdeel wat je wilt, uit twintig:
|
||
|
||
**Media beheren** — Sonarr (series), Radarr (films), Lidarr (muziek), Readarr
|
||
(boeken), Whisparr
|
||
|
||
**Indexers en ondersteuning** — Prowlarr, Jackett, FlareSolverr, Bazarr
|
||
(ondertitels)
|
||
|
||
**Downloaden** — qBittorrent, SABnzbd, NZBGet, en Gluetun om al het
|
||
downloadverkeer door een VPN te leiden
|
||
|
||
**Verzoeken en aanvullingen** — Jellyseerr, Recyclarr (TRaSH-profielen),
|
||
Unpackerr, Tdarr (hercoderen), Autobrr, Maintainerr, Huntarr
|
||
|
||
Standaard staan Sonarr, Radarr, Prowlarr, Bazarr, qBittorrent en Jellyseerr aan;
|
||
de rest zet je zelf bij.
|
||
|
||
## Gluetun schakelt de downloadclients om
|
||
|
||
Zet je Gluetun aan, dan verhuizen qBittorrent, SABnzbd en NZBGet naar diens
|
||
netwerknamespace en verliezen ze hun eigen poortmapping — precies zoals het
|
||
hoort, want anders lekt hun verkeer om de VPN heen. Hun webinterfaces bereik je
|
||
dan via de poorten van de Gluetun-container.
|
||
|
||
Daarbij zat een val: qBittorrent en SABnzbd luisteren allebei standaard op 8080,
|
||
en achter één VPN-container botsen ze. qBittorrent draait nu intern op 8090.
|
||
|
||
## Groepsschakelaars werkten niet
|
||
|
||
Een groepsschakelaar in template.json was zelf geen variabele en kreeg dus nooit
|
||
een startwaarde. Elk schakelbaar onderdeel stond daardoor standaard uit — een
|
||
stack met schakelaars zou leeg binnenkomen. Ze worden nu als veld meegegeven met
|
||
een instelbare standaardstand, en de interface toont ze alleen als schakelaar in
|
||
de kop van de groep.
|
||
|
||
---
|
||
|
||
# v0.6.00-beta — 29 apps erbij, en een bug die zes bestaande apps onstartbaar maakte
|
||
|
||
## 🐛 Named volumes verdwenen uit het compose-bestand
|
||
|
||
Bij het bouwen van de nieuwe apps liep ik tegen iets aan dat er al langer zat.
|
||
`_drop_empty_mappings` ruimt sleutels op die een `<% if %>`-blok leeg
|
||
achterlaat. Maar een named volume wordt gedeclareerd als een kale sleutel zonder
|
||
waarde:
|
||
|
||
```yaml
|
||
volumes:
|
||
immich_pgdata:
|
||
```
|
||
|
||
Die werd als "lege mapping" opgeruimd, waarna ook het bovenliggende `volumes:`
|
||
wegviel. De services verwezen vervolgens naar een volume dat nergens meer
|
||
gedeclareerd stond, en `docker compose up` weigert dat met *"refers to undefined
|
||
volume"*.
|
||
|
||
**Immich, Ghost, Miniflux, Paperless-ngx, Unifi Network en Vikunja waren
|
||
daardoor niet installeerbaar via Server Up.** Alleen bekende compose-sleutels
|
||
worden nu opgeruimd; een volumedeclaratie blijft staan.
|
||
|
||
## 📦 29 nieuwe apps
|
||
|
||
**Domotica** — Zigbee2MQTT, Mosquitto MQTT, ESPHome, Node-RED, Z-Wave JS UI,
|
||
Homebridge, Scrypted, Frigate NVR
|
||
|
||
**3D-printen** — Bambuddy (Bambu Lab, zonder cloud), OctoPrint, Spoolman
|
||
|
||
**Gezin en huishouden** — Baby Buddy, Grocy, Tandoor Recipes, Firefly III
|
||
|
||
**Media** — Audiobookshelf, Navidrome, Kavita, Calibre-Web, Jellyseerr
|
||
|
||
**Kennis** — BookStack, FreshRSS, Wallabag
|
||
|
||
**Netwerk en beheer** — WireGuard Easy, Homepage, SearXNG, Duplicati,
|
||
Watchtower, Technitium DNS
|
||
|
||
Daarmee staat de teller op 65 apps.
|
||
|
||
## ✅ Testsuite over de echte templates
|
||
|
||
Nieuwe `tests/test_apps.py` rendert **elke** app met de echte engine en
|
||
controleert de uitkomst: geldige YAML, elke service heeft een image of build,
|
||
named volumes zijn gedeclareerd, geen onvervangen `<< variabelen >>`, en geen
|
||
onbedoeld dubbele standaardpoorten. Die laatste vond zeven botsingen tussen de
|
||
nieuwe apps en bestaande; die zijn rechtgezet. Botsingen die logisch zijn
|
||
(Traefik en Nginx Proxy Manager op 80/443, AdGuard en Pi-hole op 53) staan met
|
||
uitleg in een allowlist.
|
||
|
||
---
|
||
|
||
# v0.5.50-beta — Containers die nergens te vinden waren
|
||
|
||
Het dashboard meldde 13 actieve containers en 17 images, terwijl je die nergens
|
||
terugzag. Dat kwam doordat de cijfers en de lijst uit twee verschillende bronnen
|
||
komen:
|
||
|
||
* de **tellers** komen van `docker info` en gaan over de hele Docker-daemon
|
||
* de **stacklijst** toont alleen mappen in `LIBRARY_DIR` met een compose-bestand
|
||
|
||
Alles wat je buiten Server Up om had gestart — je Forgejo, de act_runner,
|
||
containers van vóór je Server Up ging gebruiken — telde dus wel mee maar was
|
||
onzichtbaar en onbedienbaar. Een beheertool die dingen telt die hij niet toont,
|
||
is verwarrend.
|
||
|
||
## Wat er nu is
|
||
|
||
- **Overzicht van alle containers op de host.** Onder de stacklijst staat een
|
||
blok "Overige containers" met alles wat niet bij een beheerde stack hoort,
|
||
inclusief image, poorten en compose-project. Starten, stoppen, herstarten en
|
||
logs bekijken kan direct.
|
||
- **De dashboardteller vertelt nu het hele verhaal**: onder "actief" staat
|
||
hoeveel daarvan via Server Up loopt.
|
||
- De eigen container is gemarkeerd als "deze app" en kan niet via dit blok
|
||
gestopt worden — daarvoor is de herstartknop in de instellingen, die dat
|
||
netjes afhandelt in plaats van zichzelf halverwege een verzoek te stoppen.
|
||
- Een viewer ziet de lijst wel maar kan niets bedienen.
|
||
|
||
## Verholpen tijdens het bouwen
|
||
|
||
Containers zonder poorten én zonder compose-labels vielen weg uit de lijst als
|
||
ze toevallig als laatste stonden: `strip()` haalde de lege velden aan het eind
|
||
van de uitvoer weg, waarna die regel te weinig kolommen leek te hebben. Gevonden
|
||
door de test die precies zo'n container als laatste zet.
|
||
|
||
---
|
||
|
||
# v0.5.46-beta — Deploy strandde op een .env van een andere gebruiker
|
||
|
||
De stap die instellingen klaarzet faalde op `touch: cannot touch '.env':
|
||
Permission denied`. Het bestand was met sudo aangemaakt en dus van root, terwijl
|
||
de runner als gewone gebruiker draait — waarmee de hele deploy afbrak op iets
|
||
dat geen blokkade hoort te zijn.
|
||
|
||
- `SU_TAG` gaat nu altijd via de omgeving naar `docker compose`. Wegschrijven in
|
||
`.env` blijft de voorkeur omdat het een handmatige `docker compose up`
|
||
overleeft, maar 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 door.
|
||
|
||
---
|
||
|
||
# v0.5.45-beta — Wizard-knop toegevoegd
|
||
|
||
De wizard was alleen zichtbaar bij een verse installatie: zodra hij een keer was
|
||
afgerond, was er geen enkele manier meer om hem te openen. De vertaalsleutels
|
||
`wizard_open` en `wizard_reset` bestonden al en `/api/wizard/reset` werkte ook,
|
||
maar de knop is nooit gebouwd — ook niet in v0.4.60.
|
||
|
||
- Blok **Setup Wizard** onder Instellingen met "Wizard openen" en "Opnieuw
|
||
uitvoeren".
|
||
- Sluitknop in de wizard zelf; die kon je eerder alleen verlaten door hem
|
||
helemaal af te ronden.
|
||
- De staptitels lopen nu via `t()` in plaats van harde Nederlandse teksten.
|
||
|
||
---
|
||
|
||
# v0.5.44-beta — Gedetecteerd netwerk verdween uit het formulier
|
||
|
||
Zodra je een netwerk had toegevoegd, was het blok "gevonden op deze server" leeg
|
||
bij een volgende poging: subnetten die al geconfigureerd waren werden uit de
|
||
suggesties gefilterd. Dat pakt verkeerd uit — je kunt prima een tweede netwerk op
|
||
hetzelfde subnet willen met een andere range, en zonder dat blok lijkt het
|
||
formulier kapot.
|
||
|
||
- Gedetecteerde netwerken blijven staan en tonen "al toegevoegd" in plaats van
|
||
te verdwijnen.
|
||
- Levert de detectie niets op zonder dat er een fout is, dan staat er nu uitleg
|
||
in plaats van een leeg blok.
|
||
|
||
---
|
||
|
||
# v0.5.43-beta — Van-tot omrekenen naar een CIDR-blok
|
||
|
||
"Ik wil van 10.0.20.200 tot .254" is een volstrekt redelijke wens, maar bestaat
|
||
niet als CIDR-notatie: dat zijn 55 adressen, en Docker accepteert alleen
|
||
uitgelijnde blokken van 2, 4, 8, 16 … adressen.
|
||
|
||
Onder het IP-range-veld zit nu een **van-tot-hulp**. Vul de eerste en laatste
|
||
gewenste adressen in en Server Up rekent uit welke blokken in de buurt komen:
|
||
|
||
* **dekt alles** — het kleinste blok dat je hele wens omvat (pakt er onderaan
|
||
wat bij)
|
||
* **past binnen** — het grootste blok dat volledig binnen je wens valt
|
||
|
||
Bij `.200` t/m `.254` levert dat `10.0.20.192/26` op: 62 adressen, `.193` t/m
|
||
`.254`. Klikken vult het veld in.
|
||
|
||
---
|
||
|
||
# v0.5.42-beta — Netwerk toevoegen: twee blokkades weg
|
||
|
||
Een netwerk aanmaken lukte niet, met wisselend gedrag: soms een foutmelding dat
|
||
de IP-range buiten het subnet lag terwijl dat aantoonbaar niet zo was, soms geen
|
||
melding maar toch geen resultaat. Het bleken twee losse oorzaken.
|
||
|
||
## De foutmelding die niet klopte
|
||
|
||
De beoordeling gaat af bij elke toetsaanslag. Tijdens het intypen van `/28` ga
|
||
je langs `/2`, en `10.0.20.240/2` normaliseert naar `0.0.0.0/2` — inderdaad
|
||
buiten je subnet. Kwam dat antwoord ná het antwoord voor de volledige waarde
|
||
binnen, dan bleef die foutmelding staan en bleef de knop uitgeschakeld.
|
||
|
||
Er zit nu een volgnummer op: alleen het antwoord bij de laatste vraag telt. En
|
||
de knop wordt niet meer uitgeschakeld op de clientstatus — bij het opslaan
|
||
beslist de server, zodat een verouderde beoordeling je nooit kan blokkeren.
|
||
|
||
## De range die Docker weigerde
|
||
|
||
`10.0.20.200/28` heeft hostbits gezet. Python leest dat soepel als
|
||
`10.0.20.192/28`, maar Docker is streng en antwoordt *"has host bits set"*. Wij
|
||
valideerden soepel en stuurden vervolgens de ruwe tekst door, dus de interface
|
||
keurde iets goed dat bij het aanmaken alsnog strandde.
|
||
|
||
Subnet en IP-range worden nu omgezet naar hun canonieke vorm voordat ze naar
|
||
Docker gaan én voordat ze worden opgeslagen, met een waarschuwing die laat zien
|
||
wat er van je invoer gemaakt is. `.200/28` is trouwens een logische invoer — je
|
||
bedoelt "vanaf .200" — maar een /28 begint nu eenmaal op een veelvoud van 16.
|
||
|
||
---
|
||
|
||
# v0.5.41-beta — Netwerk toevoegen liep vast tijdens het typen
|
||
|
||
Een netwerk toevoegen gaf een JSON-parsefout in de browser.
|
||
|
||
Oorzaak: de live beoordeling die bij elke toetsaanslag afgaat, bouwde een lijst
|
||
van alle bruikbare adressen in het bereik. Tijdens het intypen van
|
||
`192.168.1.0/24` is de tussenstand `192.168.1.0/2` een volkomen geldig
|
||
netwerk — met 1.073.741.824 adressen. Het verzoek liep daarop vast en de browser
|
||
kreeg geen JSON meer terug.
|
||
|
||
- `review()` rekent eerste adres, laatste adres en aantal nu uit in plaats van
|
||
ze op te sommen. Antwoord binnen 0,05 seconde bij elk prefix.
|
||
- `suggest_range()` had hetzelfde probleem: die somde alle /28-blokken op, wat
|
||
bij een `/8` ruim een miljoen blokken zijn. Rekent nu van achteren naar voren.
|
||
- Waarschuwing bij een bereik van meer dan 4096 adressen, want dat is vrijwel
|
||
altijd een typefout in het prefix.
|
||
- Ook `.env` van de deploy-map werd bij elke deploy gewist door `rsync --delete`,
|
||
omdat het bestand in `.gitignore` staat en dus niet in de checkout zit. Daardoor
|
||
raakte je `BIND` telkens kwijt. `--exclude='.env'` toegevoegd.
|
||
- Nieuw `.env.example` met alle instellingen bij elkaar en uitleg per sleutel.
|
||
|
||
---
|
||
|
||
# v0.5.40-beta — Netwerken instellen zonder uitzoekwerk
|
||
|
||
## 🔍 Automatische detectie
|
||
|
||
Bij **Instellingen → Netwerken → Netwerk** toont Server Up nu bovenaan het
|
||
netwerk dat je server zelf gebruikt: interface, subnet, gateway en een
|
||
voorgestelde vrije IP-range. Eén klik vult het hele formulier.
|
||
|
||
De detectie start heel kort een container die de netwerknamespace van de host
|
||
deelt en leest daar de routetabel. Dat is nodig omdat Server Up in een eigen
|
||
namespace zit: de vorige versie las `/sys/class/net` binnen de container en
|
||
toonde daardoor de bridge-interface van de container zelf in plaats van de
|
||
netwerkkaart van de host — een lijstje dat er goed uitzag maar niet klopte.
|
||
|
||
Lukt detecteren niet (geen docker-socket, ouder image), dan staat er een nette
|
||
melding en vul je het als vanouds handmatig in.
|
||
|
||
## 💡 Live meedenken bij het invullen
|
||
|
||
Terwijl je typt controleert Server Up de combinatie en toont wat het oplevert:
|
||
*"14 adressen beschikbaar: 192.168.1.241 tot en met 192.168.1.254"*. Klopt er
|
||
iets niet, dan staat er in gewone taal bij waarom — een range buiten het subnet,
|
||
een gateway die je beter buiten het bereik kunt houden, of een range zo klein
|
||
dat er maar een paar stacks in passen.
|
||
|
||
Een knop stelt een vrij blok voor op basis van het subnet, met de gateway
|
||
ontweken.
|
||
|
||
## 📖 Uitleg waar je hem nodig hebt
|
||
|
||
In het formulier zit een uitklapbaar blok dat uitlegt hoe subnet, gateway,
|
||
IP-range en host-interface zich tot elkaar verhouden, met een concreet voorbeeld.
|
||
`docs/netwerken.md` heeft dezelfde uitleg met een schema van de adresverdeling,
|
||
een tabel met veelgebruikte ranges, en één punt dat er echt uit moet springen:
|
||
**het instellen van de IP-range in Server Up doet niets aan je router** — je moet
|
||
daar zelf het DHCP-bereik verkleinen, anders blijven dubbele adressen mogelijk.
|
||
|
||
---
|
||
|
||
# v0.5.30-beta — Rollen, app-store-filter en twee planningsfouten
|
||
|
||
## 👥 Rollen per gebruiker
|
||
|
||
Tot nu toe had elk account volledige toegang. Nu drie rollen:
|
||
|
||
| Rol | Mag |
|
||
|---|---|
|
||
| **Beheerder** | alles |
|
||
| **Operator** | stacks, containers en backups beheren — niet de instellingen, gebruikers, repo's, netwerken of modules |
|
||
| **Alleen lezen** | alleen bekijken |
|
||
|
||
- Afgedwongen in de `before_request`-guard, niet alleen in de interface.
|
||
- Je eigen wachtwoord wijzigen mag iedereen, ongeacht rol.
|
||
- De laatste beheerder kan niet gedegradeerd of verwijderd worden.
|
||
- Wie via de reverse proxy binnenkomt zonder eigen account krijgt de rol uit
|
||
`AUTH.proxy_role`; bestaat er wél een lokaal account met die naam, dan wint
|
||
dat account.
|
||
- Accounts van vóór deze versie hebben geen rol en gelden als beheerder.
|
||
|
||
## 🔒 Rechtenescalatie verholpen
|
||
|
||
Bij het wijzigen van een wachtwoord werd het hele gebruikersrecord vervangen,
|
||
waardoor het `role`-veld wegviel. Omdat een ontbrekende rol als beheerder geldt
|
||
(nodig voor bestaande accounts), kon **elke operator of viewer zichzelf tot
|
||
beheerder promoveren door zijn eigen wachtwoord te wijzigen.** Gevonden bij het
|
||
naspelen van de rollenflow; de rol blijft nu behouden en een test dekt het af.
|
||
|
||
## ⏰ Geplande backups draaiden nooit
|
||
|
||
De scheduler stempelde ook taken af die zichzelf hadden overgeslagen. De
|
||
backup-taak bewaakt zelf of het het ingestelde uur is, dus die werd afgestempeld
|
||
op het moment van de eerste tick — waarna het volgende moment 24 uur later op
|
||
precies dat verkeerde tijdstip viel. Resultaat: de geplande backup kwam nooit
|
||
aan de beurt.
|
||
|
||
Een taak stempelt nu alleen af als hij `True` teruggeeft. De backup-taak draait
|
||
daardoor op een kort interval en bepaalt zelf wanneer het zover is, zodat een
|
||
gewijzigde planning ook meteen werkt zonder herstart. Voor wekelijkse backups is
|
||
`BACKUP_SCHEDULE_DAY` toegevoegd.
|
||
|
||
## 🔎 Zoeken en filteren in de app store
|
||
|
||
Zoekveld over naam, omschrijving en tags, plus klikbare tagfilters (de veertien
|
||
meestgebruikte, op frequentie gesorteerd). Repo's zonder treffers vallen weg.
|
||
|
||
## 🔧 Overig
|
||
|
||
- Opstartcontrole op de meegeleverde front-end-bestanden. Mislukt het downloaden
|
||
tijdens de image-build, dan laadde de interface zonder opmaak terwijl de
|
||
server prima leek te draaien; nu staat er een duidelijke melding in het log.
|
||
- 227 tests.
|
||
|
||
---
|
||
|
||
# v0.5.20-beta — Vertalingen, SSO-scherm, complete backups en app-updates
|
||
|
||
## 🌍 Vertalingen en SSO
|
||
|
||
- De schermen uit v0.5.x (login, Beveiliging, Netwerken, Updates,
|
||
containerpaneel) gebruikten harde Nederlandse teksten terwijl de rest van de
|
||
interface via `t()` loopt. Op Engels gaf dat een mengelmoes. Nu volledig
|
||
vertaald: 208 sleutels in `nl` en `en`.
|
||
- `t()` ondersteunt plaatshouders: `t('port_taken', {port: 8080})`. Zo blijven
|
||
zinnen heel in plaats van in losse stukjes geknipt.
|
||
- Nieuw instelscherm voor **reverse-proxy-SSO** onder Instellingen →
|
||
Beveiliging: modus lokaal/proxy/beide, identiteitsheader en de vertrouwde
|
||
proxy-adressen. De backend bestond al maar was alleen met `curl` te bereiken.
|
||
- Nieuwe test bewaakt dat `nl` en `en` dezelfde sleutels houden, dat de
|
||
plaatshouders in beide talen gelijk zijn, en dat de interface geen sleutels
|
||
gebruikt die nergens gedefinieerd staan.
|
||
|
||
## 💾 Backups compleet
|
||
|
||
Backup was één `tar`-commando waarvan de returncode genegeerd werd — een
|
||
mislukte backup gold als succes, en terugzetten kon alleen met de hand.
|
||
|
||
- **Terugzetten** met één klik: stack stoppen, huidige map opzij, uitpakken,
|
||
starten. Mislukt het uitpakken, dan komt de oude situatie terug.
|
||
- **Bewaarbeleid**: aantal per stack en/of maximale leeftijd. De nieuwste
|
||
backup van een stack blijft altijd staan.
|
||
- **Geplande backups**, dagelijks of wekelijks, via een eigen planner in de
|
||
applicatie (geen cron). Een gemiste ronde loopt bij de eerstvolgende
|
||
gelegenheid alsnog.
|
||
- **Automatisch een backup vóór het bijwerken of verwijderen** van een stack.
|
||
Mislukt die, dan gaat de actie door — anders kun je een kapotte stack niet
|
||
meer opruimen.
|
||
- Uitpakken gebeurt met `tarfile` en `filter="data"`: absolute paden en
|
||
`..`-ingangen worden geweigerd. Met een kaal `tar xzf` kon een geprepareerd
|
||
archief buiten de stackmap schrijven.
|
||
- Backups binnen dezelfde seconde overschreven elkaar; namen krijgen nu een
|
||
teller.
|
||
- `docs/backups.md` legt uit wat er wél en niet in zit — de compose- en
|
||
`.env`-bestanden, **niet** de Docker-volumes met de eigenlijke appdata.
|
||
|
||
## 📦 Updates per app
|
||
|
||
- Server Up controleert nu ook of er nieuwere images zijn voor de
|
||
geïnstalleerde stacks, met een badge op de stackkaart en een knop om te
|
||
controleren. Bijwerken doet de bestaande update-knop.
|
||
- De vergelijking loopt via de Registry API v2 (`Docker-Content-Digest`), niet
|
||
via `docker manifest inspect`: dat laatste geeft per platform een aparte
|
||
digest terug terwijl `RepoDigests` de digest van de manifest-list bevat, wat
|
||
bij elk multi-arch image permanent "update beschikbaar" zou opleveren.
|
||
- Drie statussen per service — `update`, `current` en `unknown`. Een lokaal
|
||
gebouwd image, een nog niet gepulld image of een onbereikbare registry levert
|
||
`unknown` op en dus géén badge, zodat er nooit ten onrechte een update wordt
|
||
gemeld.
|
||
- Dagelijkse achtergrondcheck via dezelfde planner; resultaten 6 uur gecached.
|
||
|
||
---
|
||
|
||
# v0.5.10-beta — Update-systeem met kanalen en één-klik bijwerken
|
||
|
||
## 🔄 Kanalen
|
||
|
||
Server Up kent nu twee update-kanalen in plaats van een vinkje "pre-releases
|
||
meenemen":
|
||
|
||
- **stable** — alleen echte releases (`v0.5.10`)
|
||
- **beta** — ook pre-releases (`v0.5.10-beta1`), die `release.yml` automatisch
|
||
als zodanig markeert bij een tag met een streepje
|
||
|
||
Een release telt hoger dan zijn eigen beta (`0.5.10-beta1` < `0.5.10`), dus wie
|
||
op beta zit krijgt de definitieve versie alsnog aangeboden. De oude instelling
|
||
`UPDATE_INCLUDE_PRERELEASE` migreert automatisch naar het beta-kanaal.
|
||
|
||
## ⬇️ Bijwerken vanuit de interface
|
||
|
||
Nieuw: **Nu bijwerken** haalt het image uit je Forgejo container-registry en
|
||
vervangt de eigen container.
|
||
|
||
- Omdat een container zichzelf niet kan hercreëren, doet Server Up alleen het
|
||
voorwerk (image ophalen, `SU_TAG` wegschrijven) en laat het de hercreatie over
|
||
aan een korte helper-container die op het nieuwe image draait — dat bevat de
|
||
docker- en compose-CLI al.
|
||
- De deploy-map wordt uitgelezen uit de compose-labels van de eigen container,
|
||
niet geraden. Ontbreken die labels, draait de container niet vanaf een
|
||
registry-image, of is de docker-socket er niet, dan meldt de interface
|
||
precies waaróm bijwerken niet kan in plaats van iets te proberen.
|
||
- De vorige tag wordt onthouden, met een **terugrolknop** in de instellingen.
|
||
- Voortgang van de `docker pull` loopt via het bestaande job-logvenster; daarna
|
||
pollt de browser `/healthz` tot de nieuwe versie leeft.
|
||
|
||
## 🔧 Overig
|
||
|
||
- Update-check wordt een uur gecached; **Controleren** forceert een verse check.
|
||
Eerder deed elke paginalading een netwerkverzoek.
|
||
- Laatst-gecontroleerd-tijdstip zichtbaar in de interface.
|
||
- Registry-inloggegevens instelbaar voor een privé registry. Het token wordt net
|
||
als de git-tokens nooit teruggegeven door de API (alleen een `has_`-vlag) en
|
||
leeg laten betekent "ongewijzigd".
|
||
- `docker-compose.yml` gebruikt `${SU_IMAGE:-server-up}:${SU_TAG:-latest}`, zodat
|
||
image en tag los instelbaar zijn. Zonder `SU_IMAGE` blijft alles werken zoals
|
||
voorheen (lokaal bouwen).
|
||
- `deploy-prod.yml` bouwt én pusht het image in dezelfde job wanneer `SU_IMAGE`
|
||
ingesteld is. Bewust niet als aparte workflow: bij één runner zou de deploy
|
||
wachten op een build die zelf nog in de wachtrij staat.
|
||
- `build.yml` is nu alleen handmatig, voor het herbouwen van een specifieke tag.
|
||
- `docs/updates.md` toegevoegd: hoe het werkt, de complete Forgejo-instelling
|
||
(registry, tokens, variables, secrets, runners), de release-procedure voor
|
||
zowel stable als beta, terugrollen en een probleemoplostabel.
|
||
|
||
---
|
||
|
||
# v0.5.00-beta — Authenticatie, beveiliging en eigen IP-adressen
|
||
|
||
> **Let op bij het bijwerken.** Server Up heeft nu een login. Open na het
|
||
> bijwerken meteen de webinterface en maak een beheerdersaccount aan — zolang
|
||
> dat niet gebeurd is, kan iedereen die de pagina bereikt het account claimen.
|
||
> De poort wordt voortaan standaard op `127.0.0.1` gebonden; zet `BIND=0.0.0.0`
|
||
> in je `.env` als je er van buiten de server bij moet (liefst achter een
|
||
> reverse proxy met TLS — zie `docs/beveiliging.md`).
|
||
|
||
## 🔐 Authenticatie
|
||
|
||
Tot nu toe was elke `/api/*`-route open. Omdat Server Up de docker-socket als
|
||
root gebruikt, betekende dat: wie de poort kon bereiken, had root op de host.
|
||
|
||
- Lokale accounts met scrypt-gehashte wachtwoorden, sessiecookie
|
||
(`HttpOnly`, `SameSite=Strict`) en lockout na vijf mislukte pogingen.
|
||
- Optionele SSO via een reverse-proxy-header (Authelia/Authentik/Cloudflare
|
||
Access). De header wordt alleen vertrouwd vanaf een geconfigureerd proxy-IP.
|
||
- Loginscherm en eerste-account-setup in de interface; gebruikersbeheer en
|
||
wachtwoord wijzigen onder Instellingen → Beveiliging.
|
||
|
||
## 🛡️ Beveiligingsfixes
|
||
|
||
- **CSRF**: elke mutatie vereist een `X-CSRF-Token`-header. Routes die de
|
||
toestand wijzigen accepteren geen `GET` meer — `/api/docker/restart` was
|
||
eerder met een `<img>`-tag vanaf een willekeurige website te triggeren.
|
||
- **Path traversal**: `/api/store/install` controleerde de instantienaam niet;
|
||
`"instance": "../../…"` schreef buiten de library. Alle stack-, instantie- en
|
||
repo-namen lopen nu door één `safe_name()`-validatie.
|
||
- **Tokenlek**: git-tokens werden teruggegeven door `/api/repos` en
|
||
`/api/settings`. Die zijn vervangen door een `has_token`-vlag; opslaan met een
|
||
leeg veld wist het bestaande token niet meer.
|
||
- **Git-URL's**: alleen `http(s)://`, `ssh://` en `git@host:pad`. Git's
|
||
`ext::`-transport voert een shell-commando uit en wordt nu geweigerd.
|
||
- **Templates** renderen in een Jinja2-sandbox (server-side template injection).
|
||
- **SSH**: host-keys worden geverifieerd (`accept-new` + `/data/known_hosts`);
|
||
eerder stond `StrictHostKeyChecking=no`, waarmee elke MITM onzichtbaar was.
|
||
- **Modules**: repo's worden niet meer automatisch bij elke start gepulld
|
||
(`AUTO_SYNC_ON_BOOT`, standaard uit) en kunnen per repo op een commit worden
|
||
vastgezet.
|
||
- Productie-WSGI-server (waitress) i.p.v. de Flask-ontwikkelserver, limiet op
|
||
request-grootte, `ProxyFix`, en CSP/`X-Frame-Options`/`nosniff`/
|
||
`Referrer-Policy`-headers.
|
||
- Tailwind, Alpine en htmx worden meegeleverd in plaats van vanaf een CDN
|
||
geladen; Google Fonts is eruit. De interface werkt nu ook offline.
|
||
- `config.json` en de sleutel staan op 0600.
|
||
|
||
## 🌐 Stacks met een eigen IP-adres
|
||
|
||
Nieuw: geef een stack een eigen adres in je LAN in plaats van poorten op de host
|
||
(macvlan/ipvlan). Geen poortconflicten meer, apps op hun eigen standaardpoort, en
|
||
je kunt per app firewallen.
|
||
|
||
- Netwerkbeheer onder Instellingen → Netwerken (driver, host-interface, subnet,
|
||
gateway, optionele IP-range).
|
||
- Bij het installeren kies je "Poorten op de host" of een netwerk; Server Up
|
||
stelt het eerstvolgende vrije adres voor en houdt toegekende adressen vast.
|
||
- Het gerenderde compose-bestand wordt automatisch omgezet: poortmappings eruit,
|
||
netwerk met `ipv4_address` erin. De templates in `apps/` blijven ongewijzigd.
|
||
- Uitleg en valkuilen (waaronder de shim-interface die de host nodig heeft om
|
||
zijn eigen macvlan-containers te bereiken) staan in `docs/netwerken.md`.
|
||
|
||
## 🧩 Beheer per container
|
||
|
||
De stackkaart klapt uit naar de losse containers: per container starten, stoppen,
|
||
herstarten, logs bekijken en live CPU-/geheugengebruik.
|
||
|
||
## 📦 Veiliger installeren
|
||
|
||
- Poortvelden krijgen een vrij poortnummer voorgesteld — `next_free_port()`
|
||
bestond al maar werd nergens gebruikt. Bezette poorten worden gemeld.
|
||
- Velden voor tokens en wachtwoorden krijgen een genereerknop.
|
||
- Compose wordt gevalideerd (`docker compose config`) vóór het wegschrijven, dus
|
||
een typefout in de editor maakt een draaiende stack niet meer onstartbaar.
|
||
|
||
## 🔧 Overig
|
||
|
||
- Testsuite met pytest (112 tests), ook als stap in beide deploy-workflows.
|
||
- Audit-log gebruikt één gedeelde SQLite-verbinding — elke job lekte eerder een
|
||
file descriptor. Joblogs worden afgekapt op 2000 regels.
|
||
- Het `VERSION`-bestand is de enige bron voor het versienummer.
|
||
- Lichte `/healthz` voor de healthcheck in plaats van `docker info`.
|
||
- `fix-config.sh` verwijderd: bevatte een hardgecodeerd intern IP en
|
||
overschreef de configuratie van de gebruiker.
|
||
|
||
---
|
||
|
||
# v0.4.60 — Tweecijferig patch-nummer
|
||
|
||
## Versiebeleid vanaf v0.4.60
|
||
|
||
Het patch-deel (laatste cijfer) gebruikt nu twee cijfers en loopt in tientallen:
|
||
`0.4.60`, `0.4.61`, … `0.4.69`, `0.4.70`. Blijft semver-compatibel (het deel
|
||
wordt als geheel getal vergeleken, dus `0.4.60` > `0.4.6`).
|
||
|
||
---
|
||
|
||
# v0.4.6 — Kaal versienummer op alle builds
|
||
|
||
## Wijziging in v0.4.6
|
||
|
||
De deploy-workflows tonen nu overal het kale versienummer uit het `VERSION`-
|
||
bestand (of de tagnaam), zonder `-dev`/`-rc.<sha>`-suffix. Eenvoudiger te lezen;
|
||
bump bij elke wijziging gewoon `VERSION`.
|
||
|
||
---
|
||
|
||
# v0.4.5 — Releases & in-app update-check
|
||
|
||
## Nieuw in v0.4.5
|
||
|
||
### 🏷️ Automatische releases bij een tag
|
||
Push je een `v*`-tag, dan maakt de nieuwe `release.yml`-workflow automatisch een
|
||
Forgejo-**Release** aan met notes uit de bijbehorende `CHANGELOG.md`-sectie plus
|
||
een commit-overzicht sinds de vorige tag. Pre-release tags (bv. `v0.8.4-beta1`)
|
||
worden als pre-release gemarkeerd. Zo krijg je een Releases-pagina met duidelijke
|
||
changelog per versie, vergelijkbaar met GitHub Releases.
|
||
|
||
### 🔔 In-app "update beschikbaar"
|
||
Server Up vergelijkt de draaiende versie met de laatste release via de Forgejo/
|
||
GitHub Releases-API (semver-vergelijking, pre-releases optioneel). Is er een
|
||
nieuwere versie, dan verschijnt een melding in de topbar en een blok in
|
||
Instellingen → Updates met versie, release-notes en een link. Instelbaar via
|
||
`UPDATE_API_URL` (of env `SU_UPDATE_API`) en de optie "pre-releases meenemen".
|
||
|
||
### 🔖 Versiebeleid
|
||
Bump bij elke wijziging het centrale `VERSION`-bestand; tags zijn de bron voor
|
||
release-versies (`vX.Y.Z`, of `-beta`/`-rc` voor pre-releases).
|
||
|
||
---
|
||
|
||
# v0.4.4 — Nette versienummers op alle builds
|
||
|
||
## Nieuw in v0.4.4
|
||
|
||
Centraal `VERSION`-bestand (semver) is nu de bron voor het versienummer. De
|
||
deploy-workflows lezen het en bouwen:
|
||
|
||
- tag `v0.4.4` → `0.4.4` (productie, toont `v0.4.4`)
|
||
- push naar `main` → `0.4.4-rc.<sha>`
|
||
- push naar `dev` → `0.4.4-dev.<sha>`
|
||
|
||
Zo zie je voortaan het échte versienummer in de topbar i.p.v. alleen de
|
||
git-SHA (`vdev-1eab7c07`). Bump bij elke wijziging alleen nog `VERSION` (en voor
|
||
de zekerheid de fallback in `app.py`/`index.html` voor lokale runs).
|
||
|
||
---
|
||
|
||
# v0.4.3 — Auto-logo's legacy-apps + fix lege Docker Images
|
||
|
||
## Nieuw / fixes in v0.4.3
|
||
|
||
### 🖼️ Automatische logo's voor legacy-apps
|
||
Legacy-apps (eigen `app.json`/`stack.json`-stacks) krijgen nu automatisch een
|
||
logo via de dashboard-icons CDN, afgeleid uit de app-naam (bv. "Nextcloud" →
|
||
`nextcloud.png`). Een expliciete logo-URL in de metadata wint; bestaat het
|
||
geraden icoon niet, dan valt de UI terug op het emoji-icoon. Geldt voor zowel
|
||
de App Store als geïnstalleerde stacks.
|
||
|
||
### 🐛 Fix: Docker Images-pagina was leeg
|
||
De afbeeldingenlijst gebruikte `:key="img.id"`, maar meerdere tags kunnen
|
||
dezelfde image-ID delen → dubbele Alpine-keys waardoor de tabel niet rendert
|
||
(en de "geen images"-melding ook niet, want er waren wél images). De `x-for`
|
||
gebruikt nu de index als key.
|
||
|
||
---
|
||
|
||
# v0.4.2 — App-logo's bij stacks
|
||
|
||
## Nieuw in v0.4.2
|
||
|
||
Geïnstalleerde stacks tonen nu het app-logo (of een emoji-icoon als fallback),
|
||
zowel op het dashboard als op de Stacks-pagina. Bij installatie wordt het
|
||
logo/icoon van de bron-app opgeslagen in `.serverup.json` in de stack-map.
|
||
Bestaande installaties krijgen hun logo via een naam-match met de App Store,
|
||
dus ze hoeven niet opnieuw geïnstalleerd te worden. De store toonde al logo's
|
||
voor boilerplate-apps (via de selfhst/dashboard-icons CDN).
|
||
|
||
---
|
||
|
||
# v0.4.1 — Fix: styling werd niet toegepast
|
||
|
||
## Fix bovenop v0.4.0
|
||
|
||
De volledige Tailwind-stylesheet faalde stil omdat `.modal` via `@apply` de
|
||
eigen CSS-animatieklasse `anim` toepaste. Tailwind's `@apply` accepteert alleen
|
||
Tailwind-utilities, geen losse CSS-klassen, waardoor de hele Play-CDN-compilatie
|
||
afbrak en de pagina ongestyled (kaal HTML) werd geladen. De animatie staat nu
|
||
als gewone CSS-regel (`.anim, .modal { animation: … }`) los van `@apply`.
|
||
|
||
---
|
||
|
||
# v0.4.0 — Volledig nieuw UI-ontwerp (Modern SaaS)
|
||
|
||
## Nieuw in v0.4.0
|
||
|
||
### 🎨 Compleet herontworpen interface
|
||
Volledig nieuw, licht en ruim "Modern SaaS"-ontwerp (Inter-font, indigo accent,
|
||
zachte schaduwen, ronde 2xl-kaarten). Nieuwe app-shell: verticale sidebar met
|
||
merk-header, gegroepeerde navigatie met actieve indicator en live status-footer;
|
||
slanke sticky topbar met dynamische paginatitel en status-chips. Dashboard,
|
||
stacks, app store, images-/audittabellen, instellingen, modals, wizard, toasts
|
||
en het log-paneel zijn allemaal opnieuw vormgegeven. Inklapbaar menu en grote
|
||
touch-targets blijven behouden; volledig mobielvriendelijk.
|
||
|
||
### 🌗 Thema volgt systeemvoorkeur
|
||
Standaard volgt het thema de OS-voorkeur (licht/donker) en reageert live op
|
||
wijzigingen. Handmatige override via Auto / Light / Dark in Instellingen.
|
||
|
||
---
|
||
|
||
# v0.3.1 — UI-restyle + taalmodule (add-on)
|
||
|
||
## Nieuw in v0.3.1
|
||
|
||
### 🎨 Grondige UI-restyle
|
||
Grotere, touch-vriendelijke knoppen (min. 44px), ruimere spacing, grotere
|
||
typografie en kaarten. Layout gecentreerd met max-breedte voor meer lucht op
|
||
grote schermen. Volledig mobielvriendelijk.
|
||
|
||
### 📐 Inklapbaar menu (ook op desktop)
|
||
De hamburger klapt de sidebar nu ook op desktop in/uit; voorkeur wordt onthouden
|
||
(`localStorage`), hoofdinhoud en log-paneel schuiven mee. Op mobiel een
|
||
tap-to-close overlay.
|
||
|
||
### 🌐 Taalmodule als add-on
|
||
Nieuwe sectie in Instellingen → "Taalmodule": talen toevoegen, bewerken,
|
||
verwijderen en ingebouwde talen (NL/EN) dupliceren als startpunt. Editor toont
|
||
per sleutel de Engelse referentie met zoekfilter. Backend-endpoints:
|
||
`POST/DELETE /api/i18n`, `GET /api/i18n/keys`, `GET /api/i18n/<code>/raw`.
|
||
Toegevoegde talen komen als JSON in `translations/` en zijn direct beschikbaar.
|
||
|
||
---
|
||
|
||
# v0.3.01 — Boilerplates fixes + git-driven versioning
|
||
|
||
## Fixes bovenop v0.3.0
|
||
|
||
### 🔁 Auto-migratie van default-repos voor upgraders
|
||
Bestaande installaties (vanaf v0.2.x) hadden de Boilerplates-repo niet zichtbaar
|
||
in de App Store omdat hun `config.json` in de `su-data` volume al bestond en de
|
||
nieuwe `DEFAULTS["APP_REPOS"]` daardoor werd overschreven.
|
||
|
||
`core/__init__.py` `load()` doet nu een eenmalige migratie: ontbrekende
|
||
default-repos worden bij opstart aangevuld op basis van id, en gemarkeerd in
|
||
`MIGRATIONS_DONE: ["v0.3.0_default_repos"]`. Wordt direct gepersisteerd.
|
||
Verwijdert een gebruiker de Boilerplates-repo expliciet, dan komt-ie niet
|
||
automatisch terug.
|
||
|
||
### 🧹 YAML-poetsstap na boilerplate-render
|
||
Bij stacks waar de meeste optionele groepen uit staan (Authentik, Nextcloud,
|
||
etc.) liet de Jinja-render verlaten mapping-sleutels achter (`volumes:`,
|
||
`networks:`, etc. zonder kinderen). Docker-compose faalt daarop met *"block
|
||
sequence entries are not allowed in this context"*.
|
||
|
||
`core/boilerplates.py` `_tidy()` heeft nu een `_drop_empty_mappings()` substep
|
||
die in meerdere passes mapping-sleutels verwijdert die alleen worden gevolgd
|
||
door whitespace/commentaar of een sibling op gelijke/lagere indent. Werkt in
|
||
cascade.
|
||
|
||
### 🏷️ Versie komt nu uit git
|
||
`Dockerfile` accepteert `ARG SU_VERSION=dev` en bakt die in `ENV SU_VERSION` +
|
||
OCI image-label. Met `docker build --build-arg SU_VERSION=$(git describe ...)`
|
||
weet het image zijn eigen versie. De Server Up UI toont automatisch de juiste
|
||
waarde, ongeacht wat de productie-compose meegeeft.
|
||
|
||
Forgejo Actions kan een tag-push automatisch verwerken — zie
|
||
`server-up-deploy/README.md`.
|
||
|
||
## Migratie vanaf v0.3.0
|
||
```bash
|
||
docker compose build --no-cache
|
||
docker compose up -d
|
||
```
|
||
|
||
---
|
||
|
||
# v0.3.0 — UI rebuild + Boilerplates support
|
||
|
||
## Hoogtepunten
|
||
|
||
### 🎨 Nieuwe UI (Tailwind + Alpine + HTMX)
|
||
- `templates/index.html` is volledig herschreven. De handgeschreven CSS
|
||
(`--bg/--s1/...` variabelen, ad-hoc grid-classes) is vervangen door
|
||
Tailwind utility-classes met een gematchte donker/licht-palette.
|
||
- Statebeheer via Alpine.js: één `app()` component bovenop het hele document,
|
||
geen `$=document.getElementById`-spaghetti meer.
|
||
- HTMX is geladen voor toekomstige server-rendered partials. Het bestaande
|
||
fetch-RPC patroon blijft werken; HTMX kan progressief worden ingezet.
|
||
- Modals, toasts, terminal-overlay en first-run wizard zitten allemaal in
|
||
één Alpine-state — geen losse globale variabelen meer.
|
||
- Mobiele sidebar gedraagt zich nu correct (slide-in i.p.v. layout-flip).
|
||
|
||
### 🧩 ChristianLempa Boilerplates ondersteund
|
||
Server Up herkent nu twee stack-formaten naast elkaar:
|
||
|
||
| Formaat | Detectie | Bron |
|
||
|---|---|---|
|
||
| **Compose** (origineel) | `compose.yml` / `docker-compose.yml` (+ optioneel `stack.json`) | bes-r/server-up |
|
||
| **Boilerplate** (nieuw) | `template.json` + `files/` | ChristianLempa/boilerplates-library |
|
||
|
||
Nieuw bestand `app/core/boilerplates.py`:
|
||
- `is_boilerplate(d)` — detectie
|
||
- `metadata(d)` — converteert `template.json["metadata"]` (incl. selfhst-icons) naar Server-Up formaat
|
||
- `fields(d)` — flattened variable-schema voor de install-modal
|
||
- `render_to_dir(src, dest, values)` — rendert `files/*.yaml` met de Jinja-achtige
|
||
`<< var >>` + `<%- if expr %>` syntax die de boilerplates gebruiken (Jinja2 met
|
||
custom delimiters). Niet-tekst bestanden worden verbatim gekopieerd.
|
||
|
||
`app/core/git.py` → `_scan_compose_dirs` herkent beide formaten en zet
|
||
`format: "boilerplate"` in de stack-entry zodat de UI er een badge bij kan tonen.
|
||
|
||
`app/app.py`:
|
||
- `_find_stack_src` accepteert ook boilerplate-mappen.
|
||
- `POST /api/store/preview` retourneert voor boilerplates het variabelen-schema
|
||
(`fields`) plus een gerenderde preview met defaults.
|
||
- `POST /api/store/install` met body `{values: {...}}` rendert de templates
|
||
voor je voordat de stack gestart wordt.
|
||
|
||
### ⚙️ Default-repos
|
||
Een nieuwe Server Up komt nu uit de doos met twee app-repositories:
|
||
1. `bes-r/server-up` — eigen stacks, submap `apps`
|
||
2. `ChristianLempa/boilerplates-library` — community templates, submap `compose`
|
||
|
||
### 📦 Versie / Dockerfile
|
||
- `SU_VERSION` = `0.3.0` in `Dockerfile` en `docker-compose.yml`
|
||
- `requirements.txt`: Jinja2 expliciet toegevoegd (was al een Flask-dep)
|
||
|
||
## Migratie vanaf v0.2.29
|
||
- Build opnieuw: `docker compose up -d --build --no-cache`
|
||
- Bestaande stacks blijven werken (legacy formaat is intact).
|
||
- De Boilerplates-repo wordt automatisch toegevoegd voor verse installs.
|
||
Bestaande gebruikers kunnen de repo handmatig toevoegen via
|
||
Instellingen → Git Repositories met submap `compose`.
|
||
|
||
## Bekende beperkingen
|
||
- Boilerplate-templates met onbekende custom-filters of complexe Ansible-style
|
||
conditionals kunnen falen — de fout verschijnt in het terminal-paneel.
|
||
- `volumes:` blokken die conditioneel zijn (`<%- if volume_mode == 'local' %>`)
|
||
werken; complexere render-logica (loops over services) is nog niet getest.
|
||
- HTMX is geladen maar de meeste interacties draaien nog op fetch-RPC. Verdere
|
||
migratie naar server-rendered partials kan stapsgewijs in volgende releases.
|