MyInvoice MyInvoice.cz Manuál
Stáhnout PDF Zpět na hlavní stránku

3. Instalace — Docker

Docker je nejrychlejší cesta (cca 3 minuty) a doporučená pro nové instalace. Předpoklady: Docker Desktop (Windows / macOS) nebo Docker Engine + compose-plugin (Linux).

Klon repa je společný krok pro většinu variant:

git clone https://github.com/radekhulan/myinvoice.git myinvoice
cd myinvoice

Pak si vyber variantu podle toho, jestli chceš stavět image lokálně, nebo si stáhnout pre-built z GHCR.

3.1 Varianta A — pre-built image z GHCR (rychlejší, bez local buildu)

Stáhne hotový multi-arch image (ghcr.io/radekhulan/myinvoice:latest, linux/amd64 + linux/arm64). Nepotřebuješ na hostu pnpm/composer ani několikaminutový build.

# Linux / macOS
cmd/docker-ghcr.sh

# Windows PowerShell
.\cmd\docker-ghcr.ps1

Skript docker-ghcr postupně:

  1. Vygeneruje .env s náhodnými DB hesly (28 znaků base64)
  2. Vygeneruje cfg.docker.php z cfg.sample.php (host=db / redis, randomized app.pepper + secret_encryption_key, dev-friendly cookies)
  3. docker compose pull — stáhne image z GHCR
  4. up -d + počká na DB healthy + spustí migrace

Používá docker-compose.production.yml (image-only, žádný build: block), takže další compose příkazy vyžadují flag -f docker-compose.production.yml (viz § 3.6 Daily ops).

💡 V produkci pinuj konkrétní verzi — uprav docker-compose.production.yml a změň :latest na konkrétní tag (např. :1.7.0). Update pak přes cmd/docker-update.{sh,ps1} (auto-detekuje registry mode = pull + up -d + migrace).

Aktualizace na novou verzi:

# Linux / macOS
cmd/docker-update.sh

# Windows PowerShell
.\cmd\docker-update.ps1

Skript v registry módu sám zavolá docker compose pull app (stáhne nový image z GHCR), restartuje stack a doběhne pending migrace. Volumes (DB data) zůstávají zachovány. Režim detekuje z image běžícího kontejneru: ghcr.io/... → registry (pull), lokální build → source (git pull + rebuild). Když stack zrovna neběží, ale máš lokálně stažený GHCR image, bere to taky jako

Nový image se publikuje automaticky při každém release tagu v*.*.*, takže aktualizace je otázkou jednoho příkazu.

🔔 Upgrade přímo z UI: admin vidí v Systém → Aktualizace stav verze + tlačítko *Aktualizovat*, které pull image + restart spustí přes host-side watcher. Detaily včetně instalace watcheru jako systemd unit / Scheduled Task → § 3.9 Update watcher nebo kapitola Aktualizace. Pro denní kontrolu nové verze nezapomeň naplánovat php api/bin/cron-version-check.php (1× denně, viz Aktualizace).

WSL2 / Linux po klonu: pokud ./cmd/docker-ghcr.sh hlásí Permission denied nebo /usr/bin/env: 'bash\r': No such file…, má tvůj git zapnutý core.autocrlf=true, který na checkoutu konvertuje LF → CRLF. Oprav jednorázově existující soubory a vypni autocrlf globálně (na Linuxu nikdy nemá být zapnutý): ``bash sed -i 's/\r$//' cmd/*.sh chmod +x cmd/*.sh git config --global core.autocrlf input ` Repo má .gitattributes s *.sh text eol=lf, takže příští git clone` bude LF i bez tohoto kroku.

3.2 Varianta B — build z source

Postaví image lokálně z repa — vhodné pro vývoj a vlastní úpravy.

# Linux / macOS
cmd/docker-install.sh

# Windows PowerShell
.\cmd\docker-install.ps1

Skript docker-install postupně:

  1. Vygeneruje .env s náhodnými DB hesly (28 znaků base64)
  2. Vygeneruje cfg.docker.php z cfg.sample.php (host=db / redis, randomized app.pepper + secret_encryption_key, dev-friendly cookies pro HTTP loopback)
  3. Postaví image myinvoice:latest (multi-stage: Vue build → composer → PHP 8.5 + nginx + php-fpm z Dockerfile.alpine)
  4. Spustí stack: app (nginx:80 → host:8080) + db (MariaDB 11)
  5. Počká, až bude DB healthy, a spustí migrace

3.3 Varianta C — bez klonování repa (jen Docker)

Pokud nechceš mít na hostiteli klon repa (typicky produkční Linux server, jen Docker daemon), GHCR image obsahuje veškerý PHP/JS kód i migrace — z repa potřebuješ jen 3 malé soubory.

Varianta C1 — one-click přes docker-ghcr.sh (doporučeno)

Stáhne si i instalační skript a chová se stejně jako Varianta A (random hesla, vygenerovaný cfg.docker.php, pull image, migrace):

mkdir myinvoice && cd myinvoice
curl -O https://raw.githubusercontent.com/radekhulan/myinvoice/master/docker-compose.production.yml
curl -O https://raw.githubusercontent.com/radekhulan/myinvoice/master/cfg.sample.php
curl -O https://raw.githubusercontent.com/radekhulan/myinvoice/master/cmd/docker-ghcr.sh
chmod +x docker-ghcr.sh
./docker-ghcr.sh

Skript najde docker-compose.production.yml v aktuálním adresáři, takže nemusíš nic přejmenovávat. Update na novou verzi:

docker compose -f docker-compose.production.yml pull
docker compose -f docker-compose.production.yml up -d

Varianta C2 — manuálně, bez skriptu

Když chceš plnou kontrolu nad cfg.docker.php a .env:

mkdir myinvoice && cd myinvoice
curl -O https://raw.githubusercontent.com/radekhulan/myinvoice/master/docker-compose.production.yml
curl -O https://raw.githubusercontent.com/radekhulan/myinvoice/master/cfg.sample.php
mv docker-compose.production.yml docker-compose.yml
cp cfg.sample.php cfg.docker.php
# uprav cfg.docker.php — minimálně:
#   db.host => 'db', db.user => 'myinvoice', db.pass => '<heslo z .env níže>'
#   app.pepper a secret_encryption_key (oboje: openssl rand -base64 32)

cat > .env <<EOF
DB_PASSWORD=$(openssl rand -base64 28)
DB_ROOT_PASSWORD=$(openssl rand -base64 28)
EOF

docker compose up -d
docker compose exec app php api/bin/migrate.php

🛈 Od image v3.1.0 se v Dockeru migrace spouští automaticky při startu kontejneru (docker-entrypoint.sh). Ruční php api/bin/migrate.php zůstává bezpečný idempotentní fallback.

⚠️ Varianta C2 NEgeneruje hesla a secrets automaticky — musíš je do cfg.docker.php doplnit ručně. Pro one-click bez klonu repa použij C1.

📖 Manuál na /manual: GHCR image má vygenerovaný HTML manuál i PDF (tools/generateManualHtml.php + tools/exportManualToPdf.php se volají build-time v Dockerfile), takže http://localhost:8080/manual funguje bez dalších kroků a v sidebaru je button „Stáhnout PDF". Update na nový obsah = cmd/docker-update.{sh,ps1} (pull novějšího image z GHCR stáhne i nové vygenerované kapitoly). Kdyby /manual vrátil 503 *„Manuál není zatím vygenerovaný“*, regeneruješ manuál ručně bez rebuildu: ``bash docker compose -f docker-compose.production.yml exec app \ php tools/generateManualHtml.php docker compose -f docker-compose.production.yml exec app \ php tools/exportManualToPdf.php ``

3.4 Po dokončení (všechny varianty)

Otevři: 👉 http://localhost:8080

V prohlížeči naskočí setup wizard — viz 6. První spuštění.

⚠️ Použij http://, ne https://, a explicitní port :8080. Docker stack běží na plain HTTP — pokud zadáš https://... nebo defaultní port, dostaneš SSL_ERROR_RX_RECORD_TOO_LONG / ERR_SSL_PROTOCOL_ERROR. Pro HTTPS na LAN/produkčním serveru viz § 3.8 HTTPS / TLS terminace.

🌐 Přístup z jiného stroje (LAN IP, hostname)? Setup wizard funguje z libovolného hostu (např. http://10.0.0.8:8080) a app.url se automaticky uloží podle URL, kterou v wizardu použiješ. Pokud potřebuješ URL znát už před setupem (např. produkční doména + reverzní proxy), spusť kontejner s -e MYINVOICE_APP_URL=https://invoice.example.com. 🛈 Přístup z LAN přes IP (např. http://192.168.1.50:8080) automaticky funguje. RFC1918 privátní IP (10.*, 172.16-31.*, 192.168.*), 127.*, localhost a *.local jsou vyjmuty z HTTPS redirectu v .htaccess a web.config. Také požadavek s hlavičkou X-Forwarded-Proto: https (reverse proxy s TLS terminací) redirect přeskočí.

3.5 Změna portu

Edituj .env (vznikl po prvním spuštění):

APP_PORT=9000          # místo 8080
DB_PORT=3308           # místo 3307 (vázán jen na 127.0.0.1)

a docker compose up -d. URL pak http://localhost:9000.

3.5.1 Runtime env pro auto-migrace (Docker)

Vstupní skript image podporuje tyto proměnné:

MYINVOICE_SKIP_MIGRATIONS=1     # vypne auto-migraci při startu
MYINVOICE_MIGRATE_ATTEMPTS=20   # počet retry pokusů migrace
MYINVOICE_MIGRATE_DELAY=3       # pauza mezi pokusy (sekundy)
MYINVOICE_DATA_DIR=/data        # default v compose souborech; sjednocuje
                                # log/, storage/, private/ a cfg.local.php pod /data
MYINVOICE_AUTH_REQUIRE_TOTP=true # vynutit 2FA pro všechny uživatele
                                # (default false; viz § 37.2.4)

Default je 20 pokusů s pauzou 3 sekundy. Pokud proměnné nenastavíš, použije se výchozí chování.

MYINVOICE_DATA_DIR je default v docker-compose.yml i docker-compose.production.yml (single-volume layout app-data:/data). Drží log/, storage/, private/dkim/ i cfg.local.php — per-instance konfigurace z setup wizardu tak přežije image update. Viz § 3.5.3 Single-volume úložiště níže. Pokud upgraduješ z 3.5.x nebo staršího 3-volume layoutu, cmd/docker-update.{sh,ps1} detekuje starý layout a před up -d automaticky spustí cmd/docker-migrate-volumes.{sh,ps1} — viz § 40.5.

cfg.docker.php mount je nově volitelný — image obsahuje stub cfg.php (<?php return [];) a vše lze předat přes ENV (12-factor). Pro full-ENV deploy (Railway, Heroku, Fly.io) bind-mount ./cfg.docker.php:/var/www/html/cfg.php:ro v docker-compose.yml zakomentuj nebo odstraň.

3.5.2 Railway / PaaS specifika

Některé PaaS (typicky Railway) injectují nevyřešené placeholdery jako ${VAR}, pokud proměnná není definovaná. MyInvoice je v env overridech ignoruje, takže nepřepíší validní hodnoty z cfg.php/cfg.docker.php. Pokud chybí secret_encryption_key, aplikace fallbackuje na HKDF z app.pepper.

3.5.3 Single-volume úložiště

🛈 TL;DR: od 3.6.0 je single-volume default. Všechen stateful obsah (log/, storage/, private/dkim/ + cfg.local.php) leží v jediném persistent volumu app-data:/data. Image updaty jsou tak bezpečné — per-instance konfigurace přežije.

Layout (3.6.0+):

VlastnostSingle-volume
Volumeapp-data (+ db-data pro MariaDB)
Mount point/data
EnvMYINVOICE_DATA_DIR=/data
Composedocker compose up -d (default)
Backupjeden tar czf nad app-data + dump DB
Image updatebezpečný — cfg.local.php v /data přežije

Co je pod /data. Aplikace přes Config::applyDataDirOverrides() přepíše:

Žádné jiné cesty se nemění (kód, vendor, web/dist zůstávají uvnitř /var/www/html, čistě read-only).

Pro novou instalaci

cmd/docker-install.{sh,ps1} použije default docker-compose.yml se single-volume layoutem — nemusíš nic nastavovat navíc.

Ověření, že běží single-volume layout:

docker compose exec app sh -c 'echo $MYINVOICE_DATA_DIR'   # → /data
docker compose exec app ls /data                            # → log  storage  private  cfg.local.php (po setupu)
docker volume ls | grep myinvoice                           # vidíš pouze app-data + db-data

Pro existující 3-volume instalaci (upgrade z ≤ 3.5.x)

Nikdy nepřepínej layout bez migrace — aplikace by nahlížela do prázdného /data a tvářila se, že data zmizela. cmd/docker-update.{sh,ps1} to dělá automaticky před up -d. Detaily v § 40.5 Migrace na single-volume layout.

Shrnutí: cmd/docker-migrate-volumes.{sh,ps1} snapshotne cfg.local.php z běžícího kontejneru, zkopíruje data ze starých volumes do nového app-data přes dočasný alpine sidecar a obnoví cfg.local.php. Staré volumes nesmaže (musíš ručně po ověření). Skript je idempotentní.

Backup single-volume layoutu

docker run --rm \
  -v myinvoice_app-data:/data:ro \
  -v "$PWD":/backup \
  alpine tar czf /backup/myinvoice-data-$(date +%F).tar.gz -C /data .

Plus dump MariaDB (viz § 40.7 Záloha a obnova) — to jsou dohromady dvě entity k zálohování (db + app-data).

3.6 Daily ops

docker compose up -d                                 # start
docker compose down                                  # stop (data v named volumes přežijí)
docker compose down -v                               # stop + WIPE volumes (ZNIČÍ DB!)
docker compose logs -f app                           # live logs
docker compose exec app bash                         # shell do kontejneru
docker compose exec app php api/bin/migrate.php      # CLI uvnitř kontejneru
cmd/docker-build.sh --no-cache                       # rebuild image (po PHP/JS změnách, jen Varianta B)

💡 Pokud jsi instaloval přes Variantu A (docker-ghcr), všechny docker compose příkazy potřebují flag -f docker-compose.production.yml, např. docker compose -f docker-compose.production.yml logs -f app.

3.7 Volitelný Redis

docker compose --profile redis up -d

a v cfg.docker.php nastav redis.enabled => true. Restart appky.

3.8 HTTPS / TLS terminace

Docker stack sám TLS nedělá — web server (nginx) uvnitř kontejneru poslouchá na portu 80 (HTTP) a mapuje se na host port 8080. Pokud potřebuješ HTTPS (LAN server, produkce, doménové jméno), postav před stack reverse proxy s TLS terminací.

Symptom špatné konfigurace: prohlížeč hodí SSL_ERROR_RX_RECORD_TOO_LONG (Firefox) nebo ERR_SSL_PROTOCOL_ERROR (Chrome) — znamená to, že browser mluví TLS, ale server odpovídá plain HTTP.

Tři rozumné cesty:

  1. Caddy (nejjednodušší) — automatický Let's Encrypt pro doménu nebo self-signed pro IP, jeden Caddyfile řádek:
   vase-domena.cz {
       reverse_proxy localhost:8080
   }
  1. Nginx + self-signed cert (mkcert nebo openssl) — pro intranet bez veřejného doménového jména.
  1. Cloudflare Tunnel / Tailscale Funnel — pokud chceš veřejný přístup bez otevírání portů na firewallu.

Konkrétní recept — Caddy jako další container vedle stacku:

V kořeni repa (vedle docker-compose.production.yml) vytvoř Caddyfile:

faktury.tvojefirma.cz {
    reverse_proxy localhost:8080
}

Pak Caddy spusť na host síti, aby viděl port 8080:

docker run -d --name caddy --restart unless-stopped \
  --network host \
  -v "$PWD/Caddyfile:/etc/caddy/Caddyfile:ro" \
  -v caddy_data:/data \
  -v caddy_config:/config \
  caddy:2

Caddy si vyžádá Let's Encrypt cert sám (potřebuje veřejně dostupné porty 80/443 a A/AAAA záznam pro doménu). Auto-renewuje. X-Forwarded-Proto: https posílá automaticky — to je důležité, protože .htaccess v repu bez tohoto hlavičky vynucuje HTTP→HTTPS redirect a vzniká redirect loop.

A v cfg.docker.php přepni production nastavení:

'app' => [
    'url' => 'https://faktury.tvojefirma.cz',  // doslova to, co user vidí v adresáku
    ...
],
'session' => [
    'cookie_secure'   => true,
    'cookie_name'     => '__Host-myinvoice_session',
    'cookie_samesite' => 'Lax',
],

app.url se používá v emailových odkazech (faktury, reset hesla, upomínky) — musí přesně odpovídat veřejné URL, jinak budou linky vést na špatnou doménu nebo localhost:8080. __Host- cookie prefix vyžaduje HTTPS — pokud jsi po této změně zkusil load přes http://, login se rozbije (cookie se neuloží).

Restart stacku: docker compose -f docker-compose.production.yml restart app (nebo bez -f flagu pro Variantu B).

3.9 Update watcher — jednoclick upgrade z UI (volitelné)

Admin vidí v Systém → Aktualizace stav verze + tlačítko *Aktualizovat*, které zařadí upgrade do fronty. Aby ho někdo aplikoval, musí na hostu běžet watcher — proces, který přes docker compose exec poslouchá flag soubor uvnitř kontejneru a spouští cmd/docker-update.(sh/ps1). Bez watcheru tlačítko *Aktualizovat* nikam nedojede (UI zůstane věčně ve stavu „Upgrade probíhá…") a musíš upgrade aplikovat ručně přes shell.

Test režim (foreground)

Než ho udělej daemon, otestuj ho v běžícím okně:

# Linux / macOS
cd /opt/myinvoice
bash cmd/docker-update-watcher.sh
# Windows — spusť tím PowerShellem, který máš (uprav cd na SVOU instalační cestu)
cd C:\inetpub\myinvoice
pwsh -NoProfile -ExecutionPolicy Bypass -File cmd\docker-update-watcher.ps1
# nemáš-li PowerShell 7, použij místo `pwsh` příkaz `powershell` (Windows PS 5.1)

🛈 Watcher si vlastní update spouští tímtéž PowerShell hostem, pod kterým běží (pwsh i powershell), a cesty řeší z umístění skriptu — funguje proto i z jiného adresáře a na strojích jen s PowerShell 7 (pwsh).

Vidíš [watcher] start, polling storage/upgrade-requested.json inside container every 30s — od té chvíle hlídá flag. Klikni v UI „Aktualizovat" a do 30 s zachytí flag, spustí cmd/docker-update.(sh/ps1), výsledek napíše zpátky. Watcher zastav Ctrl+C.

Linux — systemd unit (produkce)

sudo tee /etc/systemd/system/myinvoice-update-watcher.service <<'EOF'
[Unit]
Description=MyInvoice update watcher
After=docker.service

[Service]
Type=simple
WorkingDirectory=/opt/myinvoice
ExecStart=/opt/myinvoice/cmd/docker-update-watcher.sh
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now myinvoice-update-watcher

Logy: journalctl -u myinvoice-update-watcher -f.

Windows — Scheduled Task (produkce)

# Uprav cestu k SVÉ instalaci. Máš-li jen Windows PowerShell 5.1, nahraď
# `pwsh.exe` za `powershell.exe`.
schtasks /create /tn "MyInvoice Update Watcher" `
  /tr "pwsh.exe -NoProfile -ExecutionPolicy Bypass -File C:\inetpub\myinvoice\cmd\docker-update-watcher.ps1" `
  /sc onstart /ru SYSTEM /rl HIGHEST
schtasks /run /tn "MyInvoice Update Watcher"

🛈 pwsh.exe musí být v PATH (dává ji tam instalátor PowerShell 7). Pokud ji Scheduled Task nenajde, zadej plnou cestu C:\Program Files\PowerShell\7\pwsh.exe, nebo použij powershell.exe (PS 5.1).

Stav úlohy: schtasks /query /tn "MyInvoice Update Watcher" /v /fo list.

Daily check pro detekci nové verze

Watcher jen reaguje na *kliknutí*. Aby admin viděl, že je dostupná nová verze (badge v patičce + status na /admin/update), musí běžet denní cron cmd/cron-version-check.(sh/cmd) — viz Aktualizace.

Plné detaily

Recovery při zaseknutém upgradu, test workflow z master, externí monitoring přes /api/version → kapitola Aktualizace.

3.10 Image: alpine/nginx (default) + Debian fallback

Pro hosting s omezeným diskem a pamětí je default image alpine/nginx — běží na php:8.5-fpm-alpine + nginx + php-fpm místo Debian/Apache. Funkčně je identický (stejné API, .htaccess přeložený 1:1 do nginx configu), jen výrazně štíhlejší:

MetrikaDebian/Apache (fallback)Alpine/nginx (default)
Velikost image (docker image inspect .Size)~293 MB~92 MB (−69 %)
RAM aplikace (idle)desítky MB (Apache prefork)~26 MB (php-fpm ondemand)
Web serverApache + .htaccessnginx

GHCR :latest (i :X.Y.Z, :X.Y) je nově alpine. Lokální build (docker compose build) staví taky alpine z Dockerfile.alpine.

Migrace existující instalace = nic navíc

/data i DB volume jsou plně kompatibilní mezi variantami (www-data má v obou uid 33). Existující Debian instalace se proto zmigruje sama při příštím updatu:

cmd/docker-update.sh        # registry: pull :latest (= alpine) + recreate; data zůstanou

Debian fallback (rollback)

Kdybys narazil na problém s alpine, vrať se na Debian/Apache (Dockerfile zůstává v repu, CI ho jen nepublikuje):

# registry (GHCR): pinni starší version tag — ≤ v4.31.0 jsou ještě Debian
#   v docker-compose.production.yml změň :latest na :4.31.0, pak pull + up -d

# source: postav Debian image lokálně z původního Dockerfile
docker build -f Dockerfile -t myinvoice:latest .
docker compose up -d

RAM tuning

Pro stroje s ~512 MB–1 GB RAM lze sáhnout na tyto proměnné (alpine entrypoint je čte při startu):

PHP_FPM_MAX_CHILDREN=4    # méně php-fpm workerů (každý ~30–60 MB); default 8
OPCACHE_MEMORY=64         # menší opcache shared paměť v MB; default 128

MariaDB tuning je už v obou compose souborech: performance-schema=OFF (~100–200 MB méně RAM), buffer pool 128 MB (RAM) a redo log 48 MB místo defaultních 96 MB (~50 MB méně na disku — fresh MariaDB data dir tak spadne z ~173 MB na ~120 MB; vlastní data faktur jsou jen jednotky MB). Pro nejmenší stroje přidej do .env:

DB_INNODB_BUFFER_POOL=64M   # RAM (buffer pool)
DB_INNODB_LOG_SIZE=32M      # disk (redo log) — ušetří dalších ~16 MB

🛈 MariaDB redo log se při startu s jinou velikostí bezpečně přesází (po čistém shutdownu), data zůstávají. Změna se projeví při příštím recreatnutí db kontejneru.

Úklid starých image (uvolnění disku)

Po updatech zůstávají osiřelé image. docker-update sám uklidí dangling vrstvy; staré tagované verze smaž explicitně:

cmd/docker-prune-images.sh --dry-run   # napřed vypiš, co by smazal
cmd/docker-prune-images.sh             # smaže obsolete (běžící + compose image chrání)

3.11 Instalace přes Portainer / Dockge (GUI, bez příkazové řádky)

Protože je image veřejný na GHCR, jde MyInvoice nasadit i čistě přes webové GUI správce kontejnerů — bez klonování repa, bez SSH, bez cfg.docker.php. Veškerá konfigurace se předává proměnnými prostředí (12-factor).

K tomu slouží samostatný compose docker-compose.portainer.yml (nemá build: ani bind-mount cfg souboru — jen image: z GHCR a environment:).

🔑 Povinné proměnné (vygeneruj a poznač si): ``bash openssl rand -base64 28 # → DB_PASSWORD openssl rand -base64 28 # → DB_ROOT_PASSWORD openssl rand -base64 32 # → MYINVOICE_PEPPER openssl rand -base64 32 # → MYINVOICE_SECRET_KEY (doporučené, jinak fallback z pepperu) ``

3.11.1 Portainer — App Template (one-click)

Nejjednodušší cesta. Přidej katalog šablon a nasaď z formuláře:

  1. Settings → App Templates → URL vlož:
   https://raw.githubusercontent.com/radekhulan/myinvoice/master/portainer-template.json

a ulož.

  1. App Templates → najdi dlaždici MyInvoice.cz → klikni.
  2. Vyplň proměnné (povinné DB hesla + pepper; ostatní mají rozumný default) → Deploy the stack.
  3. Otevři http://&lt;host&gt;:8080 → doběhne setup wizard.

Portainer si compose stáhne z repa sám (repository.stackfile = docker-compose.portainer.yml), pulne image z GHCR a spustí stack. DB migrace se spustí automaticky při startu kontejneru.

3.11.2 Portainer — ruční Stack (web editor)

Když nechceš přidávat katalog šablon:

  1. Stacks → Add stack → Web editor.
  2. Vlož obsah docker-compose.portainer.yml (zkopíruj z repa).
  3. Dole v Environment variables přidej proměnné (DB_PASSWORD, DB_ROOT_PASSWORD, MYINVOICE_PEPPER, případně MYINVOICE_SECRET_KEY, APP_PORT) → Deploy the stack.

Alternativně Add stack → Repository: URL https://github.com/radekhulan/myinvoice, Compose path docker-compose.portainer.yml.

3.11.3 Dockge

Dockge drží stacky jako reálné soubory na disku, takže pasuje na compose 1:1:

  1. + Compose → název stacku (např. myinvoice).
  2. Do editoru vlož docker-compose.portainer.yml.
  3. Do .env panelu doplň proměnné:
   DB_PASSWORD=...
   DB_ROOT_PASSWORD=...
   MYINVOICE_PEPPER=...
   MYINVOICE_SECRET_KEY=...
   APP_PORT=8080
  1. Save → Start. Logy a interaktivní terminál máš přímo v Dockge.

3.11.4 HTTPS / produkce

Default compose jede HTTP-friendly cookies (MYINVOICE_SESSION_COOKIE_SECURE=false, MYINVOICE_SESSION_COOKIE_NAME=myinvoice_session) — login funguje hned přes http://host:8080. Jakmile dáš před stack HTTPS reverse proxy (viz § 3.8), přepni v proměnných:

MYINVOICE_APP_URL=https://faktury.firma.cz
MYINVOICE_SESSION_COOKIE_SECURE=true
MYINVOICE_SESSION_COOKIE_NAME=__Host-myinvoice_session

a stack překresli (Portainer: *Update the stack* / Dockge: *Restart*). Proxy musí posílat X-Forwarded-Proto: https, jinak vznikne redirect loop.

3.11.5 Aktualizace v GUI

Nová verze = pull novějšího image + recreate, migrace doběhnou při startu:

💡 V produkci radši pinni konkrétní verzi (v compose změň :latest na :4.34.3) a aktualizuj vědomě — u účetní aplikace nedoporučuju slepý auto-update přes Watchtower na :latest. In-app upgrade z UI (Systém → Aktualizace) i host watcher z § 3.9 jsou pro Portainer/Dockge zbytečné — update je tu otázkou jednoho tlačítka.

🛈 Redis (volitelné): stack má redis službu pod profilem redis. Pro zapnutí přidej do proměnných MYINVOICE_REDIS_ENABLED=true a MYINVOICE_REDIS_HOST=redis a nasaď s aktivním profilem.