Przejdź do głównej zawartości

Instalacja

Uruchom Sancho na własnym serwerze za pojedynczą subdomeną (np. sancho.example.com) przy użyciu pre-zbudowanych obrazów GHCR i Docker Compose.

Plik Przeznaczenie
docker-compose.yaml Definicje usług (postgres, rabbitmq, redis, api, app, caddy + opcjonalnie postgres-backup, garage)
Caddyfile Konfiguracja zewnętrznego Caddy — terminacja TLS i reverse proxy
garage.toml Konfiguracja single-node Garage; używana tylko gdy aktywny jest profil garage
.env.example Szablon zmiennych środowiskowych runtime; skopiuj do .env
sancho-update.service Opcjonalny unit systemd: pobiera i restartuje kontenery api i app
sancho-update.timer Opcjonalny timer systemd: uruchamia serwis aktualizacji codziennie o 04:00 UTC

Usługi rdzeniowe (zawsze działają): postgres, rabbitmq, redis, api, app, caddy. Profile opcjonalne (domyślnie wyłączone, włączane przez COMPOSE_PROFILES w .env):

Ustaw COMPOSE_PROFILES=backup,garage (albo dowolny podzbiór) w .env przed docker compose up -d.

Browser
│ https://sancho.example.com
┌─────────────────┐
│ Caddy (80/443) │ auto-TLS via Let's Encrypt
└────────┬────────┘
┌─────────┼──────────┐
│ │ │
/api/* /v1/* /analytics/* everything else
│ │ │
▼ ▼ ▼
api:3100 PostHog app:80
│ (EU) (static SPA)
┌─────┼─────┐
│ │ │
▼ ▼ ▼
postgres rabbitmq redis
api → external: SMTP relay (or Cloudflare Email Send), Google OAuth, Nextcloud OAuth, S3, Parseur, [Stripe]

Pojedyncza subdomena, routing oparty o ścieżkę. Cookies są ograniczone do DEPLOY_HOSTNAME; HTTPS jest obowiązkowe (cookies sesji są oznaczone SameSite=Strict; Secure; HttpOnly).

  • Serwer Linux, Docker 24.0.6+ z Docker Compose v2.20+ (flaga required: false w depends_on została dodana w Compose v2.20)
  • Publiczna domena z rekordem DNS A/AAAA wskazującym na serwer
  • Otwarte porty 80 i 443 (port 80 jest potrzebny do wyzwania ACME HTTP)
  • ≥ 4 vCPU / 16 GB RAM (zalecane)
Usługa Przeznaczenie Wymagana?
Google Cloud OAuth Logowanie użytkowników Wymagana, jeśli używasz SSO Google
Nextcloud OAuth (built-in) Logowanie przez własny Nextcloud (wbudowany klient OAuth 2.0) Opcjonalna (alternatywa dla Google)
Nextcloud OIDC Logowanie przez Nextcloud z zewnętrzną aplikacją OIDC Opcjonalna (alternatywa wobec bloku OAuth)
Relay SMTP (lub Cloudflare Email Send) Email transakcyjny (reset hasła, weryfikacja) Jeden z dwóch wymagany
Storage kompatybilny z S3 Przechowywanie awatarów i obrazów dokumentów dostawy Wymagana (zewnętrzny dostawca lub wewnętrzny profil garage)
Parseur OCR dokumentów dostawy Tylko dla OCR_PROVIDER=parseur
Anthropic OCR dokumentów dostawy modelem Claude Tylko dla OCR_PROVIDER=anthropic
PostHog Analityka produktowa Opcjonalna
Axiom Agregacja logów Opcjonalna
Terminal window
openssl rand -hex 32 # API_SESSION_SECRET
openssl rand -hex 32 # API_COOKIE_SECRET
openssl rand -base64 24 # PG_PASSWORD, RABBITMQ_ADMIN_PASSWORD, ROOT_PASSWORD
Terminal window
cp .env.example .env

Zabezpiecz uprawnienia zanim wkleisz jakikolwiek sekret. Domyślna umaska zostawia .env z prawami światoodczytu (0644). Plik będzie zawierał ROOT_PASSWORD, sekrety sesji/cookie, hasła Postgresa i RabbitMQ, klucze S3, sekrety klientów OAuth oraz tokeny Cloudflare/Parseur/Axiom w postaci jawnej — ogranicz uprawnienia, zanim wpiszesz dane.

Terminal window
chmod 600 .env
chown root:root .env

chmod 600 sprawia, że plik jest czytelny tylko dla właściciela; grupa i inni nie mają żadnych uprawnień. Przy 0600 własność grupowa jest nieistotna, więc root:root jest w porządku nawet jeśli inne konta są w grupie docker. Po zapisaniu sekretów zweryfikuj ls -l .env — wynik musi zaczynać się od -rw-------.

Szablon grupuje zmienne według przeznaczenia. Wypełniaj kolejne grupy w podanej kolejności.

  • DEPLOY_HOSTNAME — publiczny FQDN, np. sancho.example.com. API używa tej wartości także do ograniczania zakresu cookies sesji oraz do budowania URL-i callback OAuth i wiadomości email, więc musi się zgadzać z URL-em odwiedzanym przez użytkowników.
  • ACME_EMAIL — adres kontaktowy, który Caddy rejestruje w Let’s Encrypt do powiadomień o wygasaniu certyfikatów.
  • SANCHO_CHANNEL — kanał wydań. Domyślnie prod (obrazy z master-a: sancho-wms-{api,app}-prod). Ustaw dev, aby śledzić pre-release-y z gałęzi develop (sancho-wms-{api,app}-dev).
  • DEPLOY_ENV — sufiks nazw bucketów S3 (sancho-env-${DEPLOY_ENV} i sancho-env-${DEPLOY_ENV}-account-1). Domyślnie prod. Nie zmieniaj po pierwszym uruchomieniu — nazwy bucketów są z niego wyprowadzane i nie są migrowane.

Wszystkie te sekrety są obowiązkowe — schemat konfiguracji API odrzuca brakujące wartości na starcie i kontener natychmiast kończy działanie. Wklej wartości z sekcji Generowanie sekretów.

  • API_SESSION_SECRET — wartość hex (min. 32 znaki), podpisuje cookies sesji.
  • API_COOKIE_SECRET — wartość hex (min. 32 znaki), podpisuje pozostałe cookies.
  • PG_PASSWORD — wartość base64, hasło użytkownika Postgres.
  • RABBITMQ_ADMIN_PASSWORD — wartość base64, hasło administratora RabbitMQ.
  • ROOT_PASSWORD — wartość base64, używana do utworzenia użytkownika root przy pierwszym uruchomieniu. Musi pozostać ustawiona przy każdym restarcie (konfiguracja jest ponownie walidowana); zmiana wartości w .env wpływa tylko na przyszłe seedy, nie na hasło istniejącego konta root.

Pozostaw wartości domyślne, chyba że masz powód, by je zmienić.

  • PG_USER — domyślnie sancho.
  • PG_DB — domyślnie sancho.
  • RABBITMQ_ADMIN_USER — domyślnie sancho.

Obie zmienne są wymagane — brak którejkolwiek zatrzymuje start API.

  • API_LOG_LEVEL — poziom logów API: trace, debug, info, warn, error lub fatal. Szablon ustawia info.
  • API_LOG_PRETTYtrue formatuje logi czytelnie dla człowieka; na produkcji pozostaw false (JSON).

Jeśli ustawione są oba, Cloudflare wygrywa.

  • SMTP_HOST — hostname relay-a.
  • SMTP_PORT — domyślnie 587.
  • SMTP_USER — opcjonalne; pozostaw puste dla relay-ów uwierzytelniających samym hasłem.
  • SMTP_PASSWORD — wymagane razem z SMTP_HOST.

Logowanie email/hasło i Google konfiguruje się w .env — pusty blok ukrywa odpowiednią opcję logowania, a aplikacja webowa odpytuje API w runtime, które metody są aktywne, więc przełączanie wymaga jedynie restartu kontenera api — bez przebudowy obrazu. Logowanie Nextcloud (OAuth i OIDC) konfiguruje się natomiast w UI Sancho (Konto → Konfiguracja) — poświadczenia, listy dozwolonych domen i mapowanie grup na role są zapisywane w bazie danych, a przycisk logowania pojawia się automatycznie, bez zmiennych .env i bez restartu. Logowanie Nextcloud musi startować z email-first formularza Sancho: API wymaga rozwiązanego adresu email jako login_hint i odrzuca callback, jeśli Nextcloud zwróci inny email. Wybierz jedną ścieżkę Nextcloud (OAuth lub OIDC); włączenie obu działa, ale wyświetla dwa identyczne przyciski i ryzykuje kolizję providerId, jeśli ten sam użytkownik zaloguje się obiema drogami.

  • ENABLE_EMAIL_AUTHtrue zostawia logowanie email/hasło włączone; ustaw false, aby wymusić SSO.
  • GOOGLE_ID / GOOGLE_SECRET — klient OAuth z Google Cloud Console → APIs & Services → Credentials → Create OAuth client → Web application.
  • Authorized redirect URI: https://<DEPLOY_HOSTNAME>/api/login/google/callback.
  • Aby przycisk się pojawił, obie wartości muszą być ustawione.

S3 (wymagane) — wybierz jeden z dwóch backendów

Section titled “S3 (wymagane) — wybierz jeden z dwóch backendów”
  • Zewnętrzny dostawca (zalecane dla produkcji) — AWS S3, Cloudflare R2, Backblaze B2, MinIO itp. Bucketów dwa sposoby na zaprowizjonowanie:

    • Utwórz buckety z wyprzedzeniem i przyznaj kluczowi dostęp na poziomie obiektów.
    • Albo nadaj kluczowi uprawnienie CreateBucket i pozwól API utworzyć je przy pierwszym starcie.
  • Wewnętrzny Garage — ustaw COMPOSE_PROFILES=garage oraz S3_ENDPOINT=http://garage:3900. Wygeneruj S3_API_KEY / S3_API_SECRET / GARAGE_RPC_SECRET przez openssl rand. Bez dodatkowych komend — API tworzy swoje buckety przy pierwszym starcie. Patrz Wewnętrzny backend S3 (Garage).

Zmienne:

  • S3_ENDPOINT — URL endpointu kompatybilnego z S3 (http://garage:3900 dla wewnętrznego Garage).
  • S3_API_KEY — access key.
  • S3_API_SECRET — secret key.
  • GARAGE_RPC_SECRET — wymagany tylko gdy garage jest w COMPOSE_PROFILES. Wygeneruj openssl rand -hex 32.

OCR_PROVIDER wybiera silnik rozpoznawania: parseur (domyślnie), custom — własna usługa HTTP, albo anthropic — ekstrakcja modelem Claude (Anthropic). Opis kontraktu dla wariantu custom znajdziesz w Własne OCR.

Dla OCR_PROVIDER=parseur zarejestruj się na parseur.com, utwórz parser dopasowany do layoutu Twoich dokumentów dostaw, skopiuj klucz API i ID parsera z ustawień parsera.

  • PARSEUR_API_KEY — z dashboardu Parseur. Wymagany tylko dla OCR_PROVIDER=parseur.
  • PARSEUR_GOODS_ISSUED_NOTE_PARSER_ID — ID docelowego parsera. Pozostaw puste, aby wyłączyć OCR dokumentów dostaw.

Dla OCR_PROVIDER=custom wymagany jest OCR_CUSTOM_BASE_URL; opcjonalnie OCR_CUSTOM_SUBMIT_PATH, OCR_CUSTOM_STATUS_PATH i OCR_CUSTOM_API_KEY. Niepoprawna kombinacja zatrzymuje start API.

Dla OCR_PROVIDER=anthropic wymagany jest ANTHROPIC_API_KEY; opcjonalnie OCR_ANTHROPIC_MODEL (domyślnie claude-opus-5). Nie ma konfiguracji parsera — skany dokumentów są wysyłane do API Anthropic w celu ekstrakcji.

Włącz przez COMPOSE_PROFILES (CSV: backup, garage lub oba).

  • COMPOSE_PROFILES — np. backup, garage lub backup,garage. Puste = uruchamiają się tylko serwisy rdzeniowe.
  • BACKUP_S3_BUCKET — nazwa bucketu na codzienny pg_dump. Wymagany tylko gdy backup jest w COMPOSE_PROFILES. API nie tworzy tego bucketu — utwórz go na zewnętrznym S3 z wyprzedzeniem, a dla wewnętrznego Garage uruchom raz docker compose exec garage /garage bucket create $BACKUP_S3_BUCKET. Patrz Kopie zapasowe.

Pojedyncze konto i kontrolowana rejestracja

Section titled “Pojedyncze konto i kontrolowana rejestracja”

Sancho self-hosted działa w trybie pojedynczego najemcy: logowania SSO (Google i Nextcloud) trafiają do konta root. Dodatkowi użytkownicy dołączają do konta root jednym z dwóch sposobów:

  • Lista dozwolonych domen — skonfiguruj w UI Sancho: Konto → Konfiguracja → Dozwolone domeny e-mail (wartości zapisywane są w bazie danych, nie w .env). Użytkownicy logujący się przez Google lub Nextcloud są automatycznie dodawani do konta root z rolą Member (bez uprawnień admina). Jeśli lista jest niepusta, domena email musi do niej pasować — próby spoza listy są odrzucane przekierowaniem na /sign-in?error=signup_disabled. Pusta lista oznacza brak ograniczenia domen dla SSO.
  • Zaproszenia — admin root może zaprosić dowolny adres email z wewnętrznej strony członków; zaproszony akceptuje przez standardowy link i dołącza do konta root z rolą wybraną przez admina. Zaproszenia omijają listę domen.

Użytkownicy Member nie mają polityki admina — mogą używać aplikacji, ale nie mogą wykonywać operacji root-only. Admin może promować ich na inną rolę z poziomu strony członków. Rejestracją email+hasło steruje ENABLE_EMAIL_AUTH (lista domen jej nie dotyczy) — ustaw false, aby ograniczyć logowanie wyłącznie do SSO.

Pozostaw puste, aby wyłączyć.

  • ROOT_EMAIL — nadpisuje adres email użytkownika root; domyślnie root_${DEPLOY_ENV}@sancho-wms.pl (dla DEPLOY_ENV=prod: root_prod@sancho-wms.pl). Ustaw przed pierwszym uruchomieniem — seed tworzy użytkownika root tylko raz.
  • POSTHOG_KEY — klucz analityki produktowej dostarczany do aplikacji webowej.
  • AXIOM_DATASET — nazwa datasetu logów Axiom.
  • AXIOM_TOKEN — token API Axiom.

Obrazy api i app są publikowane w ghcr.io/sancho-wms/* jako pakiety prywatne, więc docker compose pull wymaga poświadczeń — zarówno przy pierwszym wdrożeniu, jak i przy każdej kolejnej aktualizacji. Sancho udostępnia ci token tylko-do-odczytu ograniczony do tych pakietów; nie musisz tworzyć własnego GitHub PAT.

Zaloguj się na hoście jako użytkownik, który będzie uruchamiał docker compose. Timer aktualizacji systemd uruchamia się jako root, więc jeśli planujesz go włączyć, wykonaj poniższe jako root:

Terminal window
echo <TOKEN> | docker login ghcr.io -u sancho-wms --password-stdin

Użyj nazwy użytkownika i tokenu otrzymanych od Sancho. Poświadczenia trafiają do ~/.docker/config.json i przeżywają restart — ponowne logowanie potrzebne jest tylko po rotacji tokenu.

  1. Pobierz obrazy:

    Terminal window
    docker compose pull
  2. Uruchom cały stack:

    Terminal window
    docker compose up -d

API automatycznie aplikuje migracje i seeduje użytkownika root przy każdym starcie. Pierwsza wizyta na https://<DEPLOY_HOSTNAME> wyzwoli Caddy do żądania certyfikatu Let’s Encrypt — zwykle 10–30 sekund.

Seed tworzy użytkownika root pod adresem root_prod@sancho-wms.pl (domyślnie root_${DEPLOY_ENV}@sancho-wms.pl; nadpisywalny przez ROOT_EMAIL w .env ustawiony przed pierwszym uruchomieniem) z hasłem ROOT_PASSWORD, które ustawiłeś. Dla codziennych użytkowników preferuj SSO Google lub Nextcloud.

Ustaw POSTHOG_KEY w .env na klucz swojego projektu PostHog, a następnie docker compose up -d, aby zrestartować API. Klucz jest dostarczany do aplikacji webowej w runtime (przez /api/trpc/getAppConfig) — bez przebudowy, a usunięcie wartości później wyłączy analitykę przy następnym starcie API. Zewnętrzny Caddy już proxyuje /analytics/* do PostHog EU, więc nie jest wymagana inna konfiguracja.

Patrz Wewnętrzny backend S3 (Garage) — pełny przewodnik: kiedy używać, generowanie sekretów, aktywacja profilu, weryfikacja, rotacja poświadczeń, odtwarzanie i troubleshooting.

Cały stan trwały żyje pod ./data/ obok pliku compose. Docker auto-tworzy te katalogi przy pierwszym up; entrypointy postgres/rabbitmq/redis chownują je do właściwego uid przy pierwszym starcie, więc nie jest wymagana żadna ręczna konfiguracja. Pliki kończą jako własność uid kontenera (postgres=999, redis=999, rabbitmq=100, caddy=root), więc do tar/rsync po stronie hosta zwykle potrzebny jest sudo.

  • Folderdata/
    • Folderpostgres/
    • Folderrabbitmq/
    • Folderredis/
    • Foldercaddy/
    • Foldercaddy-config/

Patrz Self-hosted: kopie zapasowe — automatyczny serwis backupu do S3, dumpy ad-hoc i procedury odtwarzania.

Patrz Self-hosted: aktualizacja — wybór kanału wydań, manualny upgrade i timer auto-aktualizacji systemd.

  • .env musi mieć chmod 600 i być własnością użytkownika, który uruchamia docker compose. Patrz Zabezpiecz uprawnienia pliku.
  • Zmień każde domyślne hasło w .env (Postgres, RabbitMQ, użytkownik root).
  • Wystaw na firewallu tylko porty 80 i 443. Postgres, RabbitMQ, Redis i API są osiągalne wyłącznie wewnątrz sieci compose.
  • Interfejs zarządzania RabbitMQ (port 15672) nie jest wystawiony przez plik compose. Jeśli go potrzebujesz, dodaj handler w Caddyfile z basic_auth.
  • Rotowanie ROOT_PASSWORD w .env zmienia tylko seed dla świeżych instalacji — dla działającego wdrożenia zmień hasło z poziomu UI jako użytkownik root. Wartość w .env musi pozostać ustawiona, bo API re-waliduje config przy każdym restarcie.
  • Rotowanie API_SESSION_SECRET lub API_COOKIE_SECRET unieważnia wszystkie istniejące sesje — użytkownicy będą musieli zalogować się ponownie.
  • Rotowanie PG_PASSWORD / RABBITMQ_ADMIN_PASSWORD musi się odbyć najpierw w bazie/brokerze, potem w .env, potem docker compose up -d. Rotowanie samego .env zostawi API bez możliwości uwierzytelnienia.
  • Backupuj ./data/caddy; utrata klucza konta ACME może opóźnić ponowne wystawienie certyfikatu.