# Drift

> Handbok · 2026-10-30 · https://groovy.simplo.dev/handbok/drift

Installation, hälsokontroll, säkerhetskopiering, schemalagda jobb, sökning på servern, sökning över sajter och uppdatering av Grav och plugins.

> [!SUBPAGES 1 descriptions]

## Installation

En ny server från grunden: Grav och dess plugin med pluginhanteraren, temat och Groovys plugin ur paket, konfigen och det första kontot.

En ny installation byggs av en ren Grav, Gravs egna plugin från Gravs
pluginhanterare (`bin/gpm`) och Groovys fyra delar ur paket: temat
*groovy-docs* och pluginen *subsites*, *mail-auth* och *mcp*. Ingen kod
behöver kopieras från repot.

Alla kommandon på servern körs från webbroten, som den användare som
äger filerna där. Annars kan sajten inte skriva i sina egna mappar.

### Det som behövs
- **PHP** i den version Grav kräver (Grav 2.2: 8.3 eller senare), med
  tilläggen `zip`, `dom`, `xmlwriter`, `mbstring` och `gd`. `pdo_sqlite`
  behövs för sökning på servern.
- **nginx**, om installationen ska ha fler än en sajt. Regeln för flera
  sajter finns bara för nginx. Med Apache fungerar grundsajten, men inga
  subsajter.
- **En SMTP-server** för inloggningsmejlen. Alla utom super-admin loggar
  in med en länk i ett mejl.
- **Utgående HTTPS från PHP** är valfritt: webbfonter och förhandsbilder
  för inbäddade videor. Utan det används systemfonter och en vanlig
  spelare.

### Paketen
Paketen byggs från en klon av repot och hamnar i mappen `dist/`, som inte
checkas in:

```
bin/setup
bin/package
```

Det ger:

| Fil | Vad |
|---|---|
| `groovy-docs-<version>.zip` | temat |
| `subsites-<version>.zip`, `mail-auth-<version>.zip`, `mcp-<version>.zip` | pluginen |
| `groovy-multisite.zip` | `setup.php` och nginx-regeln, för flera sajter |
| `skill-groovy-docs-super-admin.zip`, `skill-groovy-docs-editor.zip` | temats skills för en AI-klient (sidan AI-klient) |
| `VERSIONS.txt` | den Grav och de plugin delarna är provade med |

Paketen byggs från det som är incheckat. Bygg dem på nytt efter en ändring
i temat eller pluginen. Kopiera dem till servern.

### Steg för steg
1. **Grav.** Hämta Grav i versionen i `VERSIONS.txt` från Gravs sida på
   GitHub (*Releases*, filen `grav-v<version>.zip`). Packa upp den så att
   mappen blir webbroten.
2. **Gravs plugin**, i versionerna i `VERSIONS.txt`. Ett plugin i taget,
   med adressen till just den versionen:

   ```
   php bin/gpm direct-install -y https://getgrav.org/download/plugins/form/<version>
   ```

   Gör så för `form`, `email`, `login`, `api`, `admin2`, `flex-objects` och
   `tntsearch`. Med `bin/gpm install` kan ett plugin bli nyare än det
   provade, eftersom ett annat plugin kräver det.

   `error`, `problems`, `shortcode-core` och `github-markdown-alerts`
   följer med Grav. Jämför deras versioner med `VERSIONS.txt`
   (`php bin/gpm version <namn>`).
3. **Temat och Groovys plugin**, ett paket i taget:

   ```
   php bin/gpm direct-install -y /sökväg/till/groovy-docs-<version>.zip
   php bin/gpm direct-install -y /sökväg/till/subsites-<version>.zip
   php bin/gpm direct-install -y /sökväg/till/mail-auth-<version>.zip
   php bin/gpm direct-install -y /sökväg/till/mcp-<version>.zip
   ```

   Utan tillgång till serverns filer: ladda upp paketen i Admin2 (på
   `/admin`, efter steg 6) under **Tools → Direct Install**. Temat är
   ungefär 6 MB. PHP tillåter ofta bara 2 MB per uppladdning, och Admin2
   svarar då *Either a "url" field or an uploaded "file" is required*.
   Höj gränsen i PHP:s inställningar först:

   ```ini
   upload_max_filesize = 64M
   post_max_size = 64M
   ```

   Starta sedan om PHP. I imagen `linuxserver/grav` hör raderna hemma i
   `/config/php/php-local.ini`. Med Easypanel kan filen läggas dit utan
   terminal: tjänsten → **Storage** → **Add File Mount**, med sökvägen
   ovan och raderna som innehåll, och sedan **Deploy**.

4. **Konfigen.** Allt i det här steget går att göra i Admin2 (på
   `/admin`, efter steg 6). Inställningarna för bilagor, skyddade grenar,
   sessionen och CSS sätter temat och `subsites` själva.
   - **Temat:** Themes → **Groovy Docs** → *Activate*. Det skriver
     `pages.theme: groovy-docs` i `user/config/system.yaml`.
   - **Felsidor:** Configuration → System → *Error handler* → *Display
     errors*: **Simple Error**. Besökare ser då inga sökvägar eller kod.
     Felen skrivs till loggen.
   - **Inloggningen:** Plugins → Login → *Site host*: serverns adress,
     utan avslutande snedstreck, till exempel `https://docs.example.com`.
     Utan den skickas inga inloggningsmejl.
     Lämna Configuration → System → Advanced → *Custom base URL* tom.
     Med flera sajter tar den bort subsajternas prefix ur länkarna. Bakom
     en proxy som sköter `https` (Easypanel) räcker *Reverse proxy setup*
     och *Site host*.
   - **tntsearch:** Plugins → TNT Search → avstängt. Pluginet ska vara
     installerat men avstängt: temat använder bara dess bibliotek, och
     pluginets egen sökning visar skyddade sidor.
   - **E-post:** Plugins → Email: *Mail engine* SMTP, serverns adress,
     port, kryptering och inloggning, och avsändaren (*From*). Prova med
     ett testmejl från webbroten:
     `php bin/plugin email test-email -t <adress>`.
   - **Språket:** en svensk installation sätter *Interface language* till
     *Svenska* under *Alla sajter*: **System-Admin** → *Temainställningar*,
     fliken General → *Language* (efter steg 6). Mejlen har en egen
     inställning: Plugins → Mail Auth → *Mail language*, `sv`. Se
     [Temainställningar → Gränssnittets språk](https://groovy.simplo.dev/handbok/utseende/installningar).

   Utan Admin2 skrivs samma värden i `user/config/system.yaml`
   (`pages.theme`, `errors.display: 0`), i
   `user/config/themes/groovy-docs.yaml` (`ui_language: sv`) och i
   `user/config/plugins/`: `login.yaml` (`site_host`), `tntsearch.yaml`
   (`enabled: false`), `email.yaml` (`from`, `mailer`) och `mail-auth.yaml`
   (`mail_language: sv`).
5. **Flera sajter.** Packa upp `groovy-multisite.zip`:
   - `setup.php` läggs i webbroten, bredvid `index.php`;
   - `nginx-multisite.conf` tas med i nginx-konfigurationens
     `server`-block med `include`. Den ska stå **efter** Gravs egna
     spärrar (`location`-raderna i `webserver-configs/nginx.conf`) och
     **före** `location /`. nginx använder den första regel som passar.
     Ändra `fastcgi_pass` i filen till serverns PHP-FPM.

   Kör `nginx -t` och ladda om nginx.
6. **Super-admin.** Det första kontot är super-admin och det enda med
   lösenord:

   ```
   php bin/plugin login new-user -u <namn> -e <adress> -l en -P b --admin-type=api -N "<Namn>" -t Admin
   ```

   Kommandot frågar efter lösenordet. Utan konto skickar Grav i stället
   den första besökaren till `/admin` för att skapa det.
7. **Schemalagda jobb.** Lägg in raden i crontab (sidan Schemalagda
   jobb).
8. **Töm cachen:** `php bin/grav cache`.

### Kontrollera
1. Öppna sajten. Den visas med temat, med Gravs exempelsida som
   startsida.
2. Logga in som super-admin på `/login`.
3. Öppna hälsokontrollen (sidan Hälsokontroll). Rätta det som står som
   *Warning*.
4. **System-Admin** → *Sites* → **Ny sajt** skapar en provsajt. Den
   första editorn får ett inloggningsmejl med en länk till sajten. Följ
   länken: editorn är inloggad i den nya sajten. Saknas `setup.php` eller
   nginx-regeln står det i *Sites* vad som saknas.

Ta bort provsajten i registret efteråt.

### Senare
- **Uppdatera temat eller ett av Groovys plugin:** bygg nya paket och
  installera dem som i steg 3. Töm cachen.
- **Grav och dess plugin** uppdateras enligt sidan Uppdatering av Grav och
  plugins.
- **Spärren mot installation i Admin** är av. Slå på den när
  installationen ska skötas enligt uppdateringsrutinen: Admin2 på
  grundsajten → Plugins → Subsites → *No installs or updates through
  Admin*. Med spärren på fungerar inte *Direct Install*.
- **MCP-servern** är avstängd. Hur den slås på står i sidan
  [AI-klient](https://groovy.simplo.dev/handbok/atkomst/ai-klient), under *För den som
  driftar*.

## Hälsokontroll

Det temat är beroende av men inte kan ställa in själv.

> [!HERO ocean compact light-text]
> ## Även maskiner behöver en hälsokontroll
>
> ... med jämna intervaller

Themes → Groovy Docs → **Health** listar det temat är beroende av men inte
kan ställa in själv. Super-admin får samma lista som JSON på
`/api/v1/docs-health`. Alla sajters hälsokontroll på en gång finns i
registret i System-Admin (se
[Skapa en sajt → Hälsokontrollen för alla sajter](https://groovy.simplo.dev/handbok/kom-igang/skapa-en-sajt)).
Varje rad är *OK*, *Warning* med vad som ska göras, eller *Not checked*.

### För site-admin: sajtens egen kontroll
**Site-Admin** i sidhuvudet → *Hälsokontroll* visar ett urval: det du som
site-admin själv kan rätta i sajten. Servern, installationen och andra
sajter ingår inte. Dem ser super-admin i listan nedan.

- **Ingen sida är mer öppen än en skyddad sida ovanför.** Flytta sidan
  ut ur den skyddade grenen om den ska vara öppen (se
  [Ã
tkomst](../../atkomst)).
- **Varje preset som en sida eller sajten pekar på finns.** Välj en
  annan preset på sidan, eller för sajten i *Inställningar*.
- **Länkar till den egna sajten är relativa.** En fullständig adress
  går sönder om sajten flyttar. Raden säger vilka sidor det gäller.
- **Ingen sida är kvar dold efter en flytt som avbröts.** Nästa flytt,
  kopia, radering eller återställning lägger oftast tillbaka den.
- **Varje editor och site-admin i sajten kan arbeta i den.** Raden
  nämner den som inte kan: ett avstängt konto, ett konto utan adress
  eller med en adress som ett annat konto också har. Rätta kontot, eller
  ta bort personen i *Members*.
- **Domänregistret listar sajten för minst en domän.** Raden finns bara
  när sajten läses via domänregistret.

En rad visas bara när den gäller sajten. Finns inget att kontrollera
står det *Inget att kontrollera i den här sajten.*

### Hela listan

- **Säkerhet:**
  - att filer på skyddade sidor verkligen är skyddade;
  - att underliggande sidor ärver skyddet;
  - att ingen sida har egna regler som är mer öppna än en skyddad sida
    ovanför (temat stänger den ändå, men då står reglerna fel);
  - att webbservern inte lämnar ut sidfiler direkt;
  - att utvecklingsinloggningen inte är på i drift;
  - att pluginet `tntsearch`, om det finns, är avstängt;
  - när sajten inte är öppen för alla (se [Ã
tkomst](../../atkomst)): att
    regeln faktiskt släpper in någon, och med grupper att varje grupp ger
    sin rättighet.
  - med pluginet `mail-auth` (se [Inloggning](https://groovy.simplo.dev/handbok/atkomst/inloggning)):
    att ingen e-postadress har två konton, att bara super-admin har
    lösenord och att länkarna i inloggningsmejlen har en fast adress.
- **Funktion:**
  - att nya CSS- och JS-filer når besökarna efter en uppdatering;
  - att dolda mappar inte blir sidor (sidhistoriken ligger i sådana);
  - PHP-tillägg och plugins som temat behöver;
  - med `mail-auth`: att inloggningsmejlen har en avsändare;
  - med subsajter: att Gravs *Custom base URL* (Configuration → System →
    Advanced) är tom. Ifylld tror Grav i en subsajt att den ligger i
    webbroten: länkarna leder till bas-sajten, och sajtväljaren markerar
    också bas-sajten som vald. Behövs `https` bakom en proxy: slå på
    *Reverse proxy setup* och fyll i *Site host* under Plugins → Login;
  - att mapparna temat skriver i går att skriva i: presets, webbfonter och,
    med redigering påslagen, sidorna och papperskorgen;
  - med sökning på servern: att det som behövs finns och att indexet är
    fyllt (se [Sökning på servern](https://groovy.simplo.dev/handbok/drift/sokning-pa-servern)).
- **Innehåll:**
  - presets som saknas;
  - fullständiga länkar till den egna sajten, som går sönder vid flytt;
  - utseendevärden i en sajts temainställningar. De har ingen verkan:
    utseendet är sajtens preset.

## Säkerhetskopiering

Vad som ska med, och vad som aldrig ska till git.

Temat sparar inga säkerhetskopior av sajten; det görs i driften, till
exempel till ett privat git-repo (pluginet Git Sync eller ett schemalagt
jobb).

- **Ska med:** sidorna, med `.history/`-mapparna (sidhistoriken; tas den
  inte med, tappar en återställd sajt all historik), sajtmapparna i
  `user/env/<namn>/`, papperskorgarna (`user/.trash/` och
  `user/env/<namn>/.trash/`), `user/config/` och presets i
  `user/data/groovy-docs/presets/`.
- **Ska aldrig till git:** `user/accounts/` (användarkonton),
  `user/config/security.yaml`, `*-private.php` och andra hemligheter i
  `user/config/`.

## Schemalagda jobb

Gravs schemaläggare, raden i crontab och jobbet som tömmer papperskorgarna.

Grav har en egen schemaläggare. Den startas av en enda rad i serverns
crontab och kör sedan de jobb som står på tur.

### Raden i crontab
Raden läggs in en gång per server, för den användare som äger filerna.
Utan raden körs inga schemalagda jobb.

1. Gå till webbroten och låt Grav skriva ut raden:
   `php bin/grav scheduler --install`.
2. Kör kommandot som skrivs ut. Det lägger till raden i crontab.
3. Kontrollera med `crontab -l`.

Raden ser ut ungefär så här, med serverns sökvägar:

```
* * * * * cd /sökväg/till/webbroten;/sökväg/till/php bin/grav scheduler 1>> /dev/null 2>&1
```

- **Den körs varje minut.** Schemaläggaren avgör själv vilka jobb som
  står på tur.
- **Utan `--env`.** Med flera sajter föreslår kommandot att du lägger till
  `--env=<namn>`. Gör inte det. Jobbet för alla sajter registreras av
  bas-sajten, och det går igenom alla sajter själv.
- **Ta bort raden** med `crontab -e`, om servern ska sluta köra jobben.

### Jobben
`php bin/grav scheduler --jobs` listar jobben, när de körs och när de
senast kördes. Förutom Gravs egna jobb, till exempel att rensa cachen,
finns:

- **Tömma papperskorgarna** (`subsites-trash-purge`). Med flera sajter i
  installationen tömmer jobbet alla sajters papperskorgar varje natt, också
  sajter där ingen redigerar. Utan jobbet töms en papperskorg bara när
  någon raderar något eller öppnar den.
  - **Hur länge något ligger kvar** ställs in en gång för alla sajter, i
    bas-sajtens Admin: Themes → Groovy Docs → Editing → Trash → *Empty trash
    after (days)*. `0` betyder aldrig. En sajts egen Admin kan inte ändra
    värdet.
  - **När jobbet körs** står i pluginet Subsites (`trash_purge_at`,
    standard kl. 03.00).
  - **Resultatet**, en rad per sajt, står i
    `logs/subsites-trash-purge.out`.

### Prova för hand
- `php bin/grav scheduler --run=subsites-trash-purge` kör ett jobb direkt,
  utan att vänta på dess tid.
- `php bin/grav scheduler -d` visar felen om ett jobb misslyckats.

## Sökning på servern

Ett val per sajt för stora sajter: servern söker i stället för läsarens webbläsare.

Som standard söker läsarens webbläsare. Första gången läsaren öppnar
sökrutan hämtas en lista över alla sidor läsaren får se, och sökningen görs
i den. I en stor sajt blir listan stor. Då kan sajten låta servern söka i
stället.

### Vad valet gör
- **I webbläsaren** (standard): sökrutan hämtar listan en gång per
  sidvisning och söker i den.
- **På servern:** servern håller ett sökindex för sajten och uppdaterar det
  efter varje ändring. Sökrutan skickar varje sökning till servern och får
  tillbaka de 20 bästa träffarna.

För läsaren ser sökrutan likadan ut: samma genväg, senaste sökningar,
taggar och versionsval. Läsaren ser bara sidor den får läsa, precis som
förut. Servern prövar varje träff mot läsarens behörighet innan den visas.

Två skillnader i vad som hittas:
- Varje ord ska finnas på sidan, som ett helt ord eller början av ett ord.
  `konfig` hittar *konfiguration*, men inte *grundkonfiguration*.
- Flera ord behöver inte stå i följd.

### När det lönar sig
Listan i webbläsaren väger kring 3 kB per sida. Mätt i en provsajt med
2 400 sidor i tre versioner:

| | I webbläsaren | På servern |
|---|---|---|
| Att hämta före första sökningen | 2,3 MB (en version), 6,7 MB (alla) | inget |
| Per sökning | inget (sökningen görs lokalt) | 0,2–7 kB, svar på 0,03–0,07 s |
| Efter en ändring i sajten | nästa hämtning tar 0,1–0,3 s | indexet uppdateras på 0,02 s |

- **Under några hundra sidor:** behåll standard. Listan är liten, och
  sökningen känns omedelbar.
- **Från ett par tusen sidor,** eller när läsarna ofta har långsam
  uppkoppling: välj servern.

Indexet på servern tar plats på disken: kring 30 MB för 2 400 sidor.

### Så slås det på
1. Installera Grav-pluginet `tntsearch` och **stäng av det**: `enabled:
   false` i `user/config/plugins/tntsearch.yaml`. Temat använder bara
   sökbiblioteket som följer med pluginet. Pluginets egen sökning ska
   aldrig vara på, eftersom den visar sidor som är skyddade.
2. PHP behöver tillägget `pdo_sqlite`, och Gravs cache ska vara på.
3. Välj i sajtens Admin: Themes → Groovy Docs → **Search** → *Search runs*
   → *On the server*. Valet gäller en sajt i taget.
4. Öppna [Hälsokontrollen](https://groovy.simplo.dev/handbok/drift/halsokontroll). Den säger om något saknas
   och om indexet är fyllt.

Indexet fylls av sig självt, efter nästa ändring eller första sökningen.
Fram till dess söker webbläsaren som förut. I en sajt med några tusen sidor
tar det ett par sökningar eller ändringar.

### Om något saknas
Saknas pluginet, `pdo_sqlite` eller cachen, söker webbläsaren som med
standardvalet. Inget går sönder, och hälsokontrollen visar en varning.

## Sökning över sajter

Ett val för hela installationen: den som förvaltar eller redigerar flera sajter kan söka i dem på en gång.

Sökrutan söker som standard i den sajt man står i. Den som arbetar med
flera sajter kan få ett val till: att söka i alla sina sajter på en gång.

### Vem kan söka över sajter
- **System-admin och super-admin:** i alla sajter.
- **En editor:** i de sajter den redigerar, och i sajten den står i.
- **Members söker aldrig över sajter,** hur många sajter de än får läsa.
  De ser inte valet.

Träffarna kommer bara från sidor personen får läsa. En sajt man inte får
läsa ger inga träffar. System-admin läser alla sajter.

### Så ser det ut
Med valet påslaget har sökrutan två knappar: *Den här sajten* och *Mina sajter*.
Valet gäller tills sidan laddas om; varje ny sida börjar med *Den här sajten*.

Med *Mina sajter*:
- Träffarna grupperas per sajt, med sajten man står i först och några
  träffar från varje sajt.
- I andra sajter söks bara den aktuella versionen. I sajten man står i
  gäller versionen man läser. Versionsväljaren döljs.
- `tag:namn` fungerar som vanligt. Har en sajt själv taggen, räknas alla
  dess sidor. I andra sajter hittas bara sidorna med taggen. Med tomt
  sökfält visas sajternas taggar under *Sajternas taggar*.

Varje sajt söker på sitt eget sätt, i webbläsaren eller på servern (se
[Sökning på servern](https://groovy.simplo.dev/handbok/drift/sokning-pa-servern)). Webbläsaren frågar högst tre
sajter åt gången.

### Så slås det på
Valet gäller hela installationen och slås på av den som driftar den:

1. Öppna bas-sajtens Admin: Plugins → Subsites.
2. Slå på **Search across sites** och spara.

En subsajt kan inte ändra valet. Av är standard; då ser sökrutan ut och
fungerar precis som förut.

## Uppdatering av Grav och plugins

Rutinen steg för steg: före, i ordning, prov mellan stegen, driftsättning och att backa.

Grav och plugins uppdateras aldrig i drift. De uppdateras lokalt, på en
egen gren, provas och driftsätts som vilken ändring som helst.

- **Kvartalsvis**, samlat.
- **Säkerhetsuppdateringar direkt**, särskilt för `login`, `api` och
  `form`. Samma rutin, men bara för det som behöver uppdateras.
- **Admin installerar och uppdaterar ingenting** när spärren är på.
  Knapparna *Install*, *Update*, *Update All*, *Upgrade Grav* och *Direct
  Install* i Admin svarar då med ett fel som hänvisar hit, också för en
  super-admin. Admin visar ändå vilka uppdateringar som finns. Spärren
  slås på i Admin2 på grundsajten → Plugins → Subsites → *No installs or
  updates through Admin*. Den är av från början.

Alla kommandon körs från webbroten, utom `bin/setup` och `bin/smoke`, som
körs från repots rot.

### Före
1. **Röktesterna är gröna** på den version som körs nu: `bin/smoke`.
   Ett test som redan är rött gör det omöjligt att se vad uppdateringen
   bröt.
2. **En egen gren**, till exempel `uppdatering-2026-q4`.
3. **En tagg** på det som körs i drift, till exempel
   `git tag fore-uppdatering-2026-q4`. Det är punkten att backa till.
4. **En säkerhetskopia** av det i `user/` som inte ligger i git: kontona
   (`user/accounts/`), `user/data/` och innehållet om det inte ligger i
   git (sidan Säkerhetskopiering).

Grav och Gravs plugin ligger inte i repot. Repots fil `versions.lock` säger
vilka versioner som gäller, och `bin/setup` hämtar dem.
5. **Se vad som finns att uppdatera:**
   `php bin/gpm version grav api login form email admin2` och övriga
   plugins. Raden säger *upgradable* eller *updatable* och vilken version.

### Ett steg i taget, i ordning
Uppdatera i den här ordningen, så att det syns vad som bröt. Pluginen
kommer före Grav-kärnan. Ett plugin kommer efter dem det beror på.

1. `form`, sedan `email`. `email` beror på `form`.
2. `login`. Det beror på `form` och `email`.
3. `api` och `admin2`, som ett steg: först `api`, direkt efter `admin2`,
   sedan röktesterna. Admin2 är byggt mot en viss version av `api`. Med
   bara det ena uppdaterat kan proven i webbläsaren falla av sig själva
   efter en stund.
4. Övriga plugins: `flex-objects`, `shortcode-core`,
   `github-markdown-alerts`, `tntsearch`, `error`, `problems`.
5. Grav-kärnan.

Temat och pluginen `subsites`, `mail-auth` och `mcp` hör till
installationen och uppdateras inte med `gpm update`. De följer med repot,
eller med nya paket på en installation byggd med paket (sidan
Installation).

För varje steg:

1. **Läs changelogen** mot listorna över vad installationen använder:
   - temat: `DEPENDENCIES.md` i temamappen;
   - pluginen `subsites`, `mail-auth` och `mcp`: avsnittet
     *Dependencies on Grav and other plugins* i deras `README.md`.

   En rad i changelogen som rör något i listorna provas särskilt.
2. **Ändra versionen** i `versions.lock`, och uppdatera `DEPENDENCIES.md`
   i temamappen.
3. **Hämta den:** `bin/setup`. Skriptet tömmer också cachen.
4. **Kör röktesterna:** `bin/smoke`.
5. **Checka in** steget för sig, med versionerna i meddelandet, till
   exempel "Uppdatera email 5.2.1 → 5.3.1". Ett steg per commit gör att
   ett enskilt steg kan backas.

Faller ett test: läs vad som väntades, jämför med changelogen och rätta
eller backa steget innan nästa.

### Ett större steg (till exempel 2.1.x → 2.2.x)
Ett byte av den andra siffran i Grav-kärnans version kan ändra mer.
Utöver stegen ovan:

1. **Läs hela changelogen**, inte bara rader som liknar listorna.
2. **Jämför serverkonfigurationen.** Ändrar uppgraderingen filerna i
   `webserver-configs/`, jämförs ändringen med installationens egen
   nginx-regel. Rör den skyddet av `user/`, provas att konfig, konton
   och data i varje sajt svarar 403.
3. **Lasttestet före och efter**, med samma scenarier, när lasttestet
   finns. Svarstiderna jämförs.
4. **Den manuella checklistan** (`tests/CHECKLIST.md`) gås igenom efter
   sista steget.

### Driftsätta
1. Grenen slås ihop när alla steg är gröna.
2. Driftsättningen är som vilken ändring som helst. På en server byggd med
   paket uppdateras Grav och Gravs plugin i samma versioner som i
   `versions.lock`.
3. **Varje driftsättning slutar med `php bin/grav cache`** på servern. Det
   gäller också en vanlig ändring utan uppdatering.
4. Öppna hälsokontrollen efteråt (sidan Hälsokontroll).

### Backa
- **Ett steg:** `git revert <commit>` för steget, sedan `bin/setup`.
- **Allt:** återställ till taggen, till exempel med
  `git revert --no-edit fore-uppdatering-2026-q4..HEAD`, och kör
  `bin/setup`. `git diff fore-uppdatering-2026-q4` ska då inte visa något.
- **Data:** ändrade en uppdatering konton eller `user/data/`, läggs
  säkerhetskopian tillbaka.
- Kör röktesterna efteråt. De ska ge samma resultat som före
  uppdateringen.
