Plik saplo.yaml

Manifest aplikacji - jeden plik opisuje stack, build, zmienne i zależności.

Plik saplo.yaml to manifest aplikacji Saplo. Opisuje cały deployment: stack, build, start, zmienne środowiskowe, domeny i zasoby. Musi znajdować się w głównym katalogu projektu (obok package.json, manage.py lub Dockerfile).

Wygeneruj go automatycznie przez saplo init (wizard) lub stwórz ręcznie wg poniższej dokumentacji.

Minimalna konfiguracja

Tylko trzy pola są wymagane. Reszta ma wartości domyślne per stack.

version: 1 name: moja-aplikacja stack: nextjs

Pełna struktura

version: 1 name: moja-aplikacja stack: nextjs app_id: 1234 runtime: node: "22" build: install: "npm ci" command: "npm run build" output: ".next" cache: - node_modules - .next/cache start: command: "npm run start" port: 3000 healthcheck: "/api/health" workers: 1 env: NODE_ENV: "production" DATABASE_URL: "@secret" packages: - pango - ffmpeg services: - redis processes: web: "gunicorn myapp.wsgi:application" worker: "celery -A myapp worker -l info" domains: - example.pl - www.example.pl hooks: pre_build: "python manage.py migrate --noinput" post_deploy: "curl -X POST $SLACK_WEBHOOK_URL -d '{\"text\":\"Deployed!\"}'" branches: production: main preview: "*" resources: ram_mb: 2048 ssd_gb: 20

Referencja pól

version

TypWymaganeWartości
integertak1

Wersja formatu saplo.yaml. Zawsze 1 w aktualnej wersji Saplo. Pozwala na przyszłe migracje formatu bez łamania kompatybilności wstecznej.

name

TypWymaganePattern
stringtak^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$

Slug aplikacji. Tylko małe litery, cyfry i myślniki. Maks 64 znaki. Nie może zaczynać ani kończyć się myślnikiem. Musi być unikalny w ramach konta. Slug jest używany jako nazwa subdomeny <name>.saploapp.pl.

name: moja-aplikacja # OK name: sklep-studiokalmus # OK name: MyApp # BLAD - wielkie litery name: -moja-app # BLAD - zaczyna sie od myslnika

stack

TypWymagane
stringtak

Stack technologiczny aplikacji. Determinuje, który builder zostanie użyty przy deploymencie.

WartośćOpisRuntime
nextjsNext.js (SSR/SSG/ISR)Node.js
reactReact/Vite - static SPA serwowany przez nginxNode.js (tylko build)
vueVue 3 + Vite - static SPA serwowany przez nginxNode.js (tylko build)
nuxtNuxt 3 SSRNode.js
sveltekitSvelteKit SSRNode.js
angularAngular - static SPA serwowany przez nginxNode.js (tylko build)
djangoPython Django + gunicorn + supervisordPython
staticHTML/CSS/JS - nginx, zero Node.jsbrak
dockerWłasny Dockerfile lub docker-compose.ymlDocker
nodeDowolna apka Node.js (Express, Fastify itp.)Node.js
astroAstro SSG/SSRNode.js

app_id

TypWymaganeZakres
integernie> 0

ID aplikacji w panelu Saplo. Generowane automatycznie przez saplo init i saplo deploy. Nie edytuj ręcznie.

runtime

Wersje runtime. Dla stacków Node.js podaj node, dla Django podaj python.

runtime: node: "22" # Next.js, React, Vue, Nuxt, SvelteKit, Angular, Node, Astro
runtime: python: "3.13" # Django
PoleTypPrzykłady
nodestring"22", "20", "18"
pythonstring"3.13", "3.12", "3.11"

Domyślne wartości per stack:

StackDomyślny runtime
nextjs, react, vue, nuxt, sveltekit, angular, node, astronode: "22"
djangopython: "3.13"
static(brak)
docker(brak - runtime w Dockerfile)

build

Konfiguracja procesu budowania aplikacji.

build.install

Komenda instalacji zależności. Wykonywana jako pierwsza w fazie build.

build: install: "npm ci" # Node.js (rekomendowane) install: "pip install -r requirements.txt" # Python/Django install: "pnpm install --frozen-lockfile" # pnpm install: "yarn install --frozen-lockfile" # yarn

build.command

Komenda budowania. Wykonywana po install, przed start.

build: command: "npm run build" command: "python manage.py collectstatic --noinput" command: ~ # brak fazy build (np. proste Node.js API)

build.output

Katalog z wynikiem buildu. Saplo kopiuje ten katalog i tworzy atomowy symlink /var/www/current - nginx nigdy nie widzi starego kodu.

build: output: ".next" # Next.js output: ".next/standalone" # Next.js standalone mode output: "dist" # React/Vue/Astro (Vite) output: ".output" # Nuxt 3 output: "build" # SvelteKit

Dla django i node pomiń to pole - cały katalog projektu jest deployowany.

build.cache

Lista katalogów cachowanych między buildami. Skraca czas instalacji z kilku minut do kilkunastu sekund.

build: cache: - node_modules # zależności npm/yarn/pnpm - .next/cache # cache buildu Next.js - .venv # virtualenv Python - .cache/pip # cache pip

Domyślne wartości build per stack:

Stackinstallcommandoutput
nextjsnpm cinpm run build.next
reactnpm cinpm run builddist
vuenpm cinpm run builddist
nuxtnpm cinpm run build.output
sveltekitnpm cinpm run buildbuild
angularnpm cinpm run build(auto)
astronpm cinpm run builddist
nodenpm ci(brak)(cały projekt)
djangopip install -r requirements.txtpython manage.py collectstatic --noinput(cały projekt)
static(brak)(brak)dist
docker(Docker build)(brak)(obraz Docker)

start

Konfiguracja uruchamiania aplikacji.

start.command

Komenda startująca serwer. Dla static sites (React, Vue, Angular, Astro SSG, static) nie podawaj - nginx serwuje pliki statyczne bez Node.js.

start: command: "npm run start" # Next.js command: "node .output/server/index.mjs" # Nuxt 3 command: "node build/index.js" # SvelteKit command: "gunicorn myproject.wsgi:application" # Django WSGI command: "gunicorn myapp.asgi:application -k uvicorn.workers.UvicornWorker" # Django ASGI command: ~ # static / SPA bez serwera

start.port

Port na którym nasłuchuje aplikacja. Saplo mapuje ten port przez NPM (nginx) na zewnątrz jako HTTPS :443.

StackDomyślny port
nextjs, nuxt, sveltekit, node3000
django8000
docker8080
react, vue, angular, astro, static80

start.healthcheck

Ścieżka HTTP GET do sprawdzenia gotowości aplikacji. Saplo odpytuje http://localhost:<port><healthcheck> po uruchomieniu. Oczekuje odpowiedzi HTTP 2xx, 301 lub 302 w ciągu 60 sekund. Brak odpowiedzi = auto-rollback do poprzedniej wersji.

start: healthcheck: "/api/health" # dedykowany endpoint (rekomendowane) healthcheck: "/" # minimum - strona główna healthcheck: ~ # wyłączony (ryzyko deploymentu zepsutej wersji)

start.workers

Liczba procesów roboczych. Dotyczy głównie Gunicorna dla Django. Dla Node.js użyj opcji --cluster bezpośrednio w komendzie.

start: command: "gunicorn myapp.wsgi:application --bind 0.0.0.0:8000" workers: 4 # 4 procesy gunicorn (zalecane: 2 * CPU + 1)

env

Zmienne środowiskowe dostępne w czasie build i runtime. Obsługuje dwa tryby: plaintext i referencja do sekretu.

env: # Plaintext - widoczne w pliku i panelu NODE_ENV: "production" NEXT_PUBLIC_API_URL: "https://api.example.pl" # Referencja do sekretu - wartość przechowywana zaszyfrowana w Saplo # Ustaw wartość przez: saplo env set DATABASE_URL="postgres://..." DATABASE_URL: "@secret" STRIPE_SECRET_KEY: "@secret"
Nie wpisuj sekretów w saplo.yaml. Plik trafia do repozytorium git. Hasła, klucze API i tokeny ustaw przez saplo env set NAZWA="wartość", a w saplo.yaml wpisz tylko "@secret" jako placeholder.

Zmienne ustawione przez saplo env set mają wyższy priorytet niż plaintext z saplo.yaml.

packages

Pakiety systemowe instalowane w kontenerze LXC przed fazami build i start. Saplo wykrywa menadżer pakietów (Alpine: apk, Debian/Ubuntu: apt).

packages: - pango # WeasyPrint - wymaga pango, cairo, gdk-pixbuf - cairo - gdk-pixbuf - ffmpeg # przetwarzanie wideo/audio - imagemagick # manipulacja obrazami - wkhtmltopdf # generowanie PDF z HTML - libpq-dev # nagłówki PostgreSQL - git # jeśli build wymaga git (np. pip install z git+...)

services

Usługi uruchamiane obok aplikacji (np. Redis jako cache lub broker Celery).

W przygotowaniu. Pole services jest częścią schematu saplo.yaml, ale silnik wdrożeń jeszcze go nie uruchamia. Redis będzie dostępny jako add-on Saplo Redis - do tego czasu użyj zewnętrznego serwera Redis (zmienna env).
NazwaPortOpis
redis6379Redis 7, używany jako cache i message broker (Celery)
# Django z Celery services: - redis env: REDIS_URL: "redis://localhost:6379/0" CELERY_BROKER_URL: "redis://localhost:6379/0"

processes

Tryb wieloprocesowy. Zamiast start.command definiuje kilka nazwanych procesów. Przydatne gdy aplikacja wymaga jednocześnie serwera web i workera (np. Celery).

W przygotowaniu. Pole processes jest częścią schematu, ale silnik wdrożeń jeszcze go nie uruchamia. Na razie użyj start.command dla pojedynczego procesu - workery (Celery beat/worker) będą wspierane wkrótce.
processes: web: "gunicorn myapp.wsgi:application --bind 0.0.0.0:8000 --workers 2" worker: "celery -A myapp worker -l info -c 2" beat: "celery -A myapp beat -l info"
Nie używaj processes i start.command jednocześnie. Przy użyciu processes pole start.command jest ignorowane.

domains

Niestandardowe domeny dla aplikacji. Subdomena <name>.saploapp.pl jest zawsze dodawana automatycznie.

domains: - example.pl - www.example.pl - app.example.pl

Saplo automatycznie tworzy proxy host w NPM, wydaje certyfikat SSL (Let's Encrypt) i konfiguruje przekierowanie HTTP na HTTPS.

Wymagane rekordy DNS u Twojego dostawcy domeny:

A example.pl 116.202.174.87 A www.example.pl 116.202.174.87

hooks

Komendy wykonywane automatycznie w określonych momentach procesu deploymentu.

hooks.pre_build

Komenda wykonywana po build.install, przed build.command. Działa w kontenerze LXC z załadowanymi zmiennymi środowiskowymi. Typowe zastosowania: migracje bazy danych, generowanie plików konfiguracyjnych.

hooks: pre_build: "python manage.py migrate --noinput"

hooks.post_deploy

Komenda wykonywana po pomyślnym healthchecku nowego deploymentu. Typowe zastosowania: notyfikacje Slack/Discord, czyszczenie cache CDN.

hooks: post_deploy: "curl -s -X POST $DISCORD_WEBHOOK -d '{\"content\":\"Nowy deployment online!\"}'"

branches

Konfiguracja gałęziowania git.

branches: production: main # gałąź uruchamiająca deployment produkcyjny preview: "*" # pattern gałęzi dla preview apps (w przygotowaniu)

Zmień na master lub inną gałąź jeśli Twoje repo tego wymaga:

branches: production: release # release branch flow

resources

Nadpisuje zasoby przydzielone przez plan. Wymaga odpowiedniego add-onu w panelu Saplo.

resources: ram_mb: 4096 # 4 GB RAM (wymaga add-onu "+2 GB RAM" jeśli plan ma 2 GB) ssd_gb: 40 # 40 GB SSD (wymaga add-onu "+25 GB SSD" jeśli plan ma 15 GB)
PoleTypMinOpis
ram_mbinteger128RAM w MB
ssd_gbinteger1Dysk SSD w GB

Kompletne przykłady per stack

Next.js (SSR standalone)

version: 1 name: ecommerce-frontend stack: nextjs app_id: 1001 runtime: node: "22" build: install: "npm ci" command: "npm run build" output: ".next/standalone" cache: - node_modules - .next/cache start: command: "node .next/standalone/server.js" port: 3000 healthcheck: "/" env: NODE_ENV: "production" NEXT_PUBLIC_API_URL: "https://api.example.pl" DATABASE_URL: "@secret" NEXTAUTH_SECRET: "@secret" domains: - example.pl - www.example.pl branches: production: main

Django (REST API + Celery)

version: 1 name: backend-api stack: django app_id: 1007 runtime: python: "3.13" build: install: "pip install -r requirements.txt" command: "python manage.py collectstatic --noinput" cache: - .venv services: - redis processes: web: "gunicorn backend.asgi:application -k uvicorn.workers.UvicornWorker --workers 2 --bind 0.0.0.0:8000" worker: "celery -A backend worker -l info -c 4" beat: "celery -A backend beat -l info" env: DJANGO_SETTINGS_MODULE: "backend.settings.production" DEBUG: "False" ALLOWED_HOSTS: "api.example.pl,backend-api.saploapp.pl" DATABASE_URL: "@secret" SECRET_KEY: "@secret" REDIS_URL: "redis://localhost:6379/0" domains: - api.example.pl hooks: pre_build: "python manage.py migrate --noinput" branches: production: main

Django z WeasyPrint (pakiety systemowe)

version: 1 name: faktury-pdf stack: django runtime: python: "3.13" packages: - pango - cairo - gdk-pixbuf - fontconfig build: install: "pip install -r requirements.txt" command: "python manage.py collectstatic --noinput" start: command: "gunicorn faktury.wsgi:application --bind 0.0.0.0:8000" port: 8000 healthcheck: "/" env: DATABASE_URL: "@secret" SECRET_KEY: "@secret" hooks: pre_build: "python manage.py migrate --noinput"

Docker Compose (multi-service)

version: 1 name: fullstack-docker stack: docker start: port: 80 healthcheck: "/" env: POSTGRES_PASSWORD: "@secret" REDIS_PASSWORD: "@secret" domains: - app.example.pl

Jeżeli w korzeniu projektu znajduje się docker-compose.yml (lub docker-compose.yaml, compose.yml, compose.yaml), Saplo automatycznie używa trybu docker compose. Saplo zapisuje zmienne z env: do pliku .env przed docker compose up, więc ${ZMIENNA} jest dostępne jako podstawienie w docker-compose.yml.

Static site

version: 1 name: landing-page stack: static build: output: "public" start: port: 80 domains: - example.pl - www.example.pl branches: production: main

Walidacja konfiguracji

Sprawdź poprawność saplo.yaml przed deploymentem:

$ saplo validate # walidacja w bieżącym katalogu $ saplo deploy --dry-run # symulacja deploymentu bez wykonania

Najczęstsze błędy

BłądPrzyczynaRozwiązanie
Missing required field: versionBrakuje version: 1Dodaj version: 1 na początku
Invalid stackNieprawidłowa wartość stackUżyj jednej z: nextjs, react, vue, nuxt, sveltekit, angular, django, static, docker, node, astro
Field "name" must be a lowercase slugWielkie litery lub spacje w nameUżyj tylko a-z, 0-9, -
saplo.yaml must be a YAML objectPusty plik lub błędna składnia YAMLSprawdź wcięcia i cudzysłowy