Alle indlæg
Guides7 min læsning

Beskriv din deployment med suble.yml

En fil pr. projektmappe beskriver en hel instans — en bygget container, et publiceret image, en managed database eller et rent OS — og `suble deploy` / `suble update` reconciler den idempotent. Sådan virker det.

TS

The Suble team

Engineering ·

suble deploy er fin til at få noget op at køre interaktivt. Men når det først findes, vil du have det i version control, reproducerbart og klar til at deploye fra CI uden et eneste spørgsmål. Det er præcis, hvad suble.yml er: en lille, deklarativ beskrivelse af en instans, som CLI'en reconciler — opret den hvis den mangler, bring den ajour hvis den findes, hver gang, på samme måde.

Hvor den kommer fra

Du behøver ikke skrive den i hånden. Den første interaktive suble deploy skriver filen for dig ud fra dine svar. suble init laver en kommenteret skabelon. Derefter reconciler suble update (i CI) filen helt uden prompts.

Filens form

En suble.yml beskriver en instans. Der er intet kind:-felt — den kildenøgle du sætter, vælger typen, så filen læser som det, du mener:

  • build: — en Docker-container bygget fra din lokale Dockerfile og pushet til dit private registry, derefter hentet på instansen
  • image: — et publiceret Docker-image (Docker Hub, GHCR, …)
  • app: — en managed 1-klik-app eller database (postgresql, mysql, coolify, …)
  • os: — et rent OS-image (Ubuntu, Debian, …)
  • template: — en ny instans fra en af dine gemte templates

Alt andet — name, plan og valgfrie blokke som ports, env, volumes, expose, firewall, networks, backup, databases og users — lægges ovenpå. Både blok- og inline-flow-stil parses, så ports: ["80:80"] og firewall: { mode: strict, allow: ["10.0.0.0/8"] } virker også.

En bygget container, eksponeret med automatisk SSL

Det typiske tilfælde: byg imaget fra dit repo, kør det, og server det på et hostname med et Let's Encrypt-certifikat, som platformen styrer for dig.

yaml
name: web-01
plan: BXS.s1
build:
  dockerfile: Dockerfile
  tag: web:latest
  container: web
ports:
  - "80:8080"
env:                     # container-env — brug ${VAR} til secrets, aldrig literaler
  NODE_ENV: production
volumes:                 # navngivne volumes overlever genskabelse af containeren
  - "data:/var/lib/app"
expose:
  domain: app.example.com
  port: 80               # Caddy står foran og henter TLS-certifikatet automatisk
firewall: smart          # off | smart | strict
backup: basic            # none | basic | extended

suble deploy bygger imaget lokalt, pusher det til dit private registry, instansen henter det (autentificeret automatisk — ingen docker login på serveren), (gen)skaber containeren, peger ingress mod den og giver dig den offentlige URL tilbage. Kører du den igen, bygges og redeployes den nyeste kode — navngivne volumes består på tværs af genskabelsen.

En managed database på et privat netværk

Sæt app: til en managed database, og du får motoren, root-legitimation (genereret server-side) og valgfrie databaser/brugere oprettet ved første deploy. Lås den til et privat netværk og en strict firewall, så kun din app kan nå den.

yaml
name: db-main
plan: CRA.m1
app: postgresql
password: ${ROOT_PASSWORD}      # OS-root-login — env-interpoleret, aldrig en literal
databases:
  - app_production              # oprettes ved første deploy
users:
  - name: app_user
    password: ${APP_DB_PASSWORD}
    database: app_production
networks:
  - my-private-net              # tilknyt via navn eller uid (tilknyt-hvis-mangler)
firewall:
  mode: strict
  allow:                        # kilde-CIDR'er (krævet ved strict)
    - 10.20.0.0/16

Hent genereret legitimation

En databases root-legitimation genereres på serveren — gemmes aldrig i din fil. Hent den når som helst med suble app info <navn> (tilføj --show-credentials i en ikke-interaktiv terminal). Secret-værdier er maskeret som standard.

Et publiceret image

Intet build-trin? Peg image: mod et hvilket som helst publiceret tag og publicer porte direkte.

yaml
name: edge
plan: BXS.s1
image: nginx:1.27
ports: ["80:80", "443:443"]

Secrets bliver uden for filen

Enhver ${VAR} udvides fra miljøet, når filen læses, så du committer din deployments form — ikke dens adgangskoder. CLI'en advarer, hvis den ser en literal secret. I CI giver du værdierne som miljøvariabler / secrets ved siden af din SUBLE_API_KEY.

Firewall: off, smart eller strict

  • off — default-allow; du kan tilføje eksplicitte rules: for at blokere bestemt trafik
  • smart — fornuftige standarder, der holder management-porte tilgængelige
  • strict — default-deny; du skal angive en allow:-liste af kilde-CIDR'er, så et headless-kørsel ikke kan låse dig ude
yaml
firewall:
  mode: off
  rules:
    - direction: in
      action: ACCEPT
      proto: tcp
      dport: "443"
    - direction: in
      action: ACCEPT
      proto: tcp
      dport: "22"
      source: 203.0.113.0/24
      comment: ssh fra kontoret

strict låser dig ikke ude

Under strict tjekker suble instance ssh din nuværende IP og tilbyder at tillade SSH fra den, før der forbindes — og en guard-IP holdes altid åben som sikkerhedsnet. Så du kan køre en låst firewall uden at miste adgang til din egen boks.

Sådan virker reconcile

deploy (interaktiv, første kørsel skriver filen) og update (CI, ingen prompts) deler en reconciler, så resultatet er ens uanset hvad:

  • Find eller opret instansen ud fra name — den genskabes aldrig, når den først findes.
  • Containere bygges / pulls igen og rulles ud — genskabes som standard, eller uden nedetid med deploy.strategy: blue-green; navngivne volumes består (hold stateful data i en managed app-database).
  • Firewall, expose (ingress + SSL) og private netværk asserteres på ny hver kørsel — netværk er tilknyt-hvis-mangler og frakobles aldrig automatisk.
  • Databaser og brugere oprettes ved første create.
  • Plan-ændringer advares der om, de auto-resizes ikke — brug suble resize bevidst.

Forhåndsvis før du anvender

Kør suble plan (eller suble update --dry-run) for at se præcis, hvad der ville ændre sig — den markerer, om en container ville blive genskabt eller rullet ud blue/green, før det sker.

Deployment-strategier: rolling, canary, blue-green & immediate

Som standard laver en redeploy nu en rolling opdatering — de nye container(e) starter ved siden af de gamle, health-checkes, og overtager først trafik, når de er sunde. Bag expose: er det uden nedetid; fejler et health-check, bliver den gamle version ved med at servere, og deployet afslutter med fejlkode (så CI fanger det). deploy:-blokken lader dig vælge en anden strategi eller justere udrulningen.

yaml
name: web
plan: BXS.s1
image: ghcr.io/me/app:1.4.2
expose:
  domain: app.example.com
  port: 8080
deploy:
  strategy: rolling      # rolling (standard) | canary | blue-green | immediate
  replicas: 3            # load-balancerede kopier (kræver expose:)
  max_unavailable: 1     # replicas pr. batch (rolling/canary) — 1 eller "25%"
  healthcheck:
    type: http           # docker (standard) | http | tcp
    path: /healthz
    port: 8080           # udledes fra expose:/ports: hvis udeladt
    status: 200
    timeout: 60          # sekunder til at blive sund, før der afbrydes
  • `rolling` (standard) — udskift replicas i batches af max_unavailable; den nye startes og health-checkes, før en gammel fjernes. Begge versioner serverer kortvarigt; uden nedetid.
  • `canary` — verificer EN ny container først (den får en andel af live-trafikken via load-balanceren); er den sund, rulles resten. Mindste blast radius for en dårlig release.
  • `blue-green` — start hele det nye sæt, health-check dem alle, og skift så al trafik på en gang. Ingen blandede versioner og øjeblikkelig rollback, men 2× replicas undervejs.
  • `immediate` — udskift alt på en gang uden health checks (kort nedetid). Den hurtige mulighed uden sikkerhedsnet.
  • `healthcheck.type`docker (container kører + stabil, respekterer imagets HEALTHCHECK), http (GET path → forvent status) eller tcp (porten accepterer en forbindelse). http/tcp-probes rammer containerens netværks-IP, så ingen publiceret port er nødvendig — de virker også for eksponerede containere. Juster timingen med initial_delay (vent før første probe, ved langsom opstart), interval, retries (fejlede probes der tolereres) og timeout.
  • `replicas: N` — kør flere load-balancerede kopier bag ingress (kræver expose:, stateless). Caddy round-robiner mellem dem, og den valgte strategi ruller hele sættet ud.

Et dårligt build erstatter aldrig et godt

Fejler den nye container sit health-check, afbrydes deployet med den gamle version stadig kørende — aldrig et halvt-ødelagt skift. Kombiner med expose: for ægte nul nedetid, og kør suble plan for at se rollout-strategien før du anvender.

Du kan også begrænse hver container med resources: (cpus/memory), så en replica ikke udsulter de andre, og deploy.drain styrer hvor mange sekunder en gammel container får til at afslutte igangværende requests, før den stoppes ved omskift (standard 10). deploy.keep beholder de seneste image-versioner på instansen, så suble rollback kan rulle tilbage til den forrige med det samme — uden ny build eller pull. Alle felter er dokumenteret i suble.yml-referencen.

I CI: push for at deploye, luk PR for at rive ned

suble update er ikke-interaktiv og idempotent — den behøver kun en projekt-API-nøgle. Et typisk workflow bygger og redeployer ved push og river miljøet ned, når en PR lukkes:

yaml
# ved push: byg + redeploy
- run: npx @suble/cli update
  env: { SUBLE_API_KEY: ${{ secrets.SUBLE_API_KEY }} }

# ved PR-luk: riv det ned
- run: npx @suble/cli destroy --yes
  env: { SUBLE_API_KEY: ${{ secrets.SUBLE_API_KEY }} }

Samme API nedenunder

suble.yml er bare en bekvem front-end til v3-API'et — det samme som dashboardet og MCP-serveren bruger, med de samme rettigheder. Alt filen gør, kunne du gøre i hånden; den gør det blot deklarativt og gentageligt.

Start med suble init (eller lad din første suble deploy skrive den), commit filen, og fra da af er hver deploy en idempotent kommando. Ny til CLI'en? Se Kom i gang med Suble CLI'en.

TS

Skrevet af

The Suble team

Engineering

Klar om få minutter. Betal pr. time.

Opret en konto og deploy din første server i dag — fra 30 kr./md.