Zum Inhalt springen

Rezepte und Apps

Rezepte

Was ein Rezept ist, dazu die Referenz für setup.json mit jedem Feld, Typ und Standardwert und dem, was ein Rezept bewusst nicht kann.

Auf dieser Seite
  1. Woher Rezepte kommen
  2. Ein vollständiges Beispiel
  3. Felder auf oberster Ebene
  4. Quelle
  5. Einstellungen (inputs)
  6. Verweise in Werten
  7. Services
  8. Images und Builds
  9. Ports
  10. Volumes
  11. Health-Checks
  12. Startreihenfolge und One-Shot-Services
  13. Internetzugang
  14. Ressourcen
  15. Expose
  16. Checks
  17. Externe Dienste (requires)
  18. Limits
  19. Fehler und Warnungen
  20. Was ein Rezept nicht ausdrücken kann

Ein Rezept ist eine kurze Datei, die Setup sagt, wie eine Software läuft: welche Programme starten, welche Einstellungen du ausfüllst, wo die Daten liegen und woran Setup erkennt, dass alles funktioniert. Wenn du einen Link einfügst, schreibt Setup das Rezept selbst und zeigt es dir, bevor irgendetwas startet. Du musst nie selbst eins schreiben.

Die Datei heißt setup.json. Sie ist einfaches JSON, du kannst sie also lesen, ändern und weitergeben. Sie beschreibt nur, was sich gefahrlos in einer Sandbox ausführen lässt. Es gibt kein Feld, mit dem eine Software an den Rechner kommt, auf dem sie läuft. Das ist Absicht.

Der Rest dieser Seite ist die Referenz für Entwickler und Maintainer.

Woher Rezepte kommen

  • Aus einer Analyse. Fügst du einen Link ein, sucht Setup zuerst nach einer setup.json im Repository, dann nach einem kuratierten Rezept im Katalog. Danach liest es Compose-Dateien, Dockerfiles und typische Projektstrukturen. Ergibt das kein sicheres Ergebnis und ist ein Modell eingerichtet, entwirft das Modell ein Rezept. Setup liest das Projekt dabei nur als Daten und führt keinen Code daraus aus.
  • Von dir eingefügt. Das Feld auf Start nimmt einen Link, eine Datei oder eine Beschreibung. Füge eine komplette setup.json ein, um sie genau so auszuführen. Fügst du eine Docker-Compose-Datei (mit ihrer .env), docker run-Befehle, einen Image-Namen, ein Dockerfile, Kubernetes-Manifeste oder eine CasaOS-, Umbrel-, Runtipi-, Cosmos- oder Portainer-Definition ein, übersetzt Setup sie in ein Rezept. Unter Erste Schritte stehen alle Arten mit Beispiel.
  • Von dir bearbeitet. Vor dem Deployment zeigt der Tab Rezept-JSON das Rezept und lässt dich es ändern. Rezepte aus dem Katalog sind dort schreibgeschützt. Füge eine bearbeitete Kopie auf Start ein, dann gehört sie dir.
  • Vom Maintainer. Projekte können eine setup.json in ihrem Repository pflegen. Mehr dazu unter Maintainer.

Ein vollständiges Beispiel

Das ist das Rezept für Umami, eine Web-Analytics-App mit PostgreSQL-Datenbank. Es besteht dieselben Schema- und Regelprüfungen, die Setup auf jedes Rezept anwendet.

json
{
  "version": 1,
  "slug": "umami",
  "name": "Umami",
  "description": "Cookie-free website analytics with a simple dashboard.",
  "category": "analytics",
  "tags": ["analytics", "privacy", "web"],
  "homepage": "https://umami.is",
  "license": "MIT",
  "source": {
    "kind": "github",
    "url": "https://github.com/umami-software/umami"
  },
  "inputs": [
    {
      "key": "DB_PASSWORD",
      "label": "Database password",
      "type": "secret",
      "generate": "password"
    },
    {
      "key": "APP_SECRET",
      "label": "App secret",
      "type": "secret",
      "generate": "hex32"
    }
  ],
  "services": {
    "db": {
      "image": "postgres:17.11-alpine",
      "env": {
        "POSTGRES_DB": "umami",
        "POSTGRES_USER": "umami",
        "POSTGRES_PASSWORD": "${inputs.DB_PASSWORD}"
      },
      "volumes": [{ "name": "postgres", "path": "/var/lib/postgresql/data" }],
      "healthcheck": {
        "command": [
          "pg_isready",
          "-h",
          "127.0.0.1",
          "-U",
          "umami",
          "-d",
          "umami"
        ]
      },
      "resources": { "cpus": 0.5, "memoryMb": 512 }
    },
    "app": {
      "image": "ghcr.io/umami-software/umami:3.4.0",
      "env": {
        "DATABASE_URL": "postgresql://umami:${inputs.DB_PASSWORD}@${services.db.host}:5432/umami",
        "APP_SECRET": "${inputs.APP_SECRET}"
      },
      "ports": [{ "container": 3000 }],
      "dependsOn": [{ "service": "db", "condition": "healthy" }],
      "resources": { "memoryMb": 512 }
    }
  },
  "expose": { "service": "app", "port": 3000 },
  "checks": {
    "http": { "path": "/api/heartbeat" },
    "startupTimeoutSeconds": 180
  },
  "notes": "Sign in with username `admin` and password `umami`, then change the password under **Settings → Profile**."
}

Beide Passwörter erzeugt Setup, wenn das Feld leer bleibt. Die Datenbank legt ihre Dateien in einem benannten Volume ab, und die App wartet, bis die Datenbank ihren Health-Check besteht. Von außen erreichbar ist nur Port 3000 von app.

Felder auf oberster Ebene

FeldTypStandardBeschreibung
$schemaStringkeinerSetup ignoriert es. Editoren mit JSON-Schema-Unterstützung können damit Vorschläge anbieten.
version1PflichtVersion des Formats. Immer 1.
slugStringPflicht1 bis 63 Zeichen: Kleinbuchstaben, Ziffern und Bindestriche, beginnt mit Buchstabe oder Ziffer. Beispiel: uptime-kuma.
nameStringPflichtAnzeigename, bis 80 Zeichen.
descriptionStringkeinerBis 500 Zeichen.
categoryStringappEins von app, ai-model, ai-app, homelab, dev-tool, database, media, productivity, analytics, security, other.
tagsString[][]Bis zu 12 Slugs.
iconURLkeinerErscheint im Katalog.
homepageURLkeinerErscheint im Katalog.
licenseStringkeinerBis 64 Zeichen, zum Beispiel MIT.
sourceObjektPflichtWoher die Software kommt.
inputsObjekt[][]Einstellungen, die jemand ausfüllt oder Setup erzeugt. Bis zu 32.
servicesObjektPflicht1 bis 8 Container, nach Service-Namen.
exposeObjektkeinerDie Webadresse, die Setup öffnet. Lass es weg, wenn die Software keine Weboberfläche hat.
checksObjekt{ "startupTimeoutSeconds": 300 }Woran Setup erkennt, dass die Software läuft.
requiresObjekt[][]Externe Dienste und Konten, die die App braucht. Bis zu 16.
notesStringkeinerMarkdown, das nach einem erfolgreichen Start erscheint, etwa die erste Anmeldung. Bis 8.000 Zeichen.

Quelle

json
"source": {
  "kind": "github",
  "url": "https://github.com/acme/widget",
  "git": { "url": "https://github.com/acme/widget.git", "ref": "v1.4.2", "subdir": "server" }
}
FeldTypStandardBeschreibung
kindStringPflichtgithub, gitlab, git, huggingface-model, huggingface-space, docs, image oder description.
urlURLkeinerDie Seite, die ein Mensch öffnen würde.
git.urlHTTPS-URLPflicht in gitClone-URL. Der Builder holt sie in seiner eigenen Sandbox, nie auf dem Runner-Host.
git.refStringRemote-HEADBranch, Tag oder vollständiger Commit-SHA.
git.subdirrelativer PfadkeinerVerzeichnis im Repository.

git ist Pflicht, sobald ein Service aus dem Quellcode baut ("build": { "context": "source" }).

Einstellungen (inputs)

Inputs sind die Werte, die jemand beim Start der App angibt, etwa eine Admin-E-Mail-Adresse oder einen API-Schlüssel. Das Dashboard zeigt sie unter Einstellungen.

json
{
  "key": "ADMIN_EMAIL",
  "label": "Admin email",
  "type": "email",
  "required": true
}
FeldTypStandardBeschreibung
keyStringPflichtUPPER_SNAKE_CASE, beginnt mit einem Buchstaben, bis 64 Zeichen. Eindeutig im Rezept.
labelStringPflichtBis 80 Zeichen.
descriptionStringkeinerHilfetext, bis 500 Zeichen.
typeStringtexttext, secret, number, boolean, select, url oder email.
requiredBooleanfalseOhne Wert startet die App nicht.
defaultStringkeinerBis 2.000 Zeichen. Immer ein String, auch bei number und boolean.
optionsString[]keinerDie Auswahl eines select, bis zu 50. Pflicht bei select.
generateStringkeinerpassword, hex32, base64-32 oder uuid. Greift, wenn das Feld leer bleibt.
lengthZahlkeiner8 bis 128: die Größe eines erzeugten Werts, für Apps, die sie prüfen (unten).

Setup nimmt für jeden Wert in dieser Reihenfolge: was eingegeben wurde, dann default, dann den Generator. Bleibt ein Input mit required leer, startet das Deployment nicht. Ein optionaler Input wird zu einem leeren String.

Danach prüft Setup den Wert gegen seinen Typ. Eine number muss sich als Zahl lesen lassen, ein boolean muss true oder false sein, ein select muss eine seiner options sein, eine url muss eine gültige URL sein und eine email muss aussehen wie name@example.com.

GeneratorErgebnis
password24 Buchstaben und Ziffern, ohne leicht verwechselbare Zeichen wie 0, O, 1, l und I.
hex3232 Zufallsbytes als 64 Hex-Zeichen.
base64-3232 Zufallsbytes, Base64-kodiert.
uuidEine zufällige UUID.

length ändert die Größe: Zeichen bei password, Zufallsbytes bei hex32 und base64-32, also die Zahl, die openssl rand -hex oder -base64 bekommt. Eine App, die einen Hex-Schlüssel mit 32 Zeichen will (openssl rand -hex 16), bekommt "generate": "hex32", "length": 16. uuid hat keine Länge.

Erzeugte Werte speichert Setup mit der App. Sie bleiben über Neustarts und Updates gleich. Werte von secret-Inputs liegen verschlüsselt in der Datenbank.

Verweise in Werten

Werte können auf Inputs, andere Services und die eigene Adresse der App verweisen.

VerweisWird zu
${inputs.KEY}Wert des Inputs KEY.
${services.NAME.host}Hostname des Services NAME im privaten Netz der App.
${deployment.url}Öffentlicher Origin des freigegebenen Services (scheme://host:port), ohne Pfad.
${deployment.host}Öffentlicher Host, ohne Schema und Port.
${deployment.port}Öffentlicher Port.
${deployment.id}ID des Deployments.

Verweise funktionieren in Werten von env, in command, entrypoint und build.args. Befehle von Health-Checks gehen unverändert durch. $${ schreibt ein wörtliches ${. Ein Verweis auf einen Input oder Service, den es nicht gibt, ist ein Fehler.

Services

services ordnet jedem Service-Namen einen Container zu. Namen beginnen mit einem Kleinbuchstaben und bestehen aus a-z, 0-9 und Bindestrichen, bis 31 Zeichen.

FeldTypStandardBeschreibung
imageStringkeinerRegistry-Referenz, zum Beispiel postgres:17.11-alpine.
buildObjektkeinerEin Image bauen statt eins zu laden. Jeder Service braucht genau eins von image und build.
entrypointString[]aus dem ImageExec-Form, bis zu 64 Teile.
commandString[]aus dem ImageExec-Form, bis zu 64 Teile.
workingDirabsoluter Pfadaus dem ImageArbeitsverzeichnis im Container.
userStringaus dem Imageuid, uid:gid oder ein Benutzername, den es im Image gibt.
envObjekt{}Umgebungsvariablen, bis zu 128. Werte bis 8.000 Zeichen.
portsObjekt[][]Bis zu 8 pro Service.
volumesObjekt[][]Bis zu 8 pro Service.
tmpfsabsoluter Pfad[][]Bis zu 8 Mounts im Arbeitsspeicher.
readOnlyRootFsBooleanfalseDas Root-Dateisystem nur lesbar einhängen.
shmSizeMbIntegerStandard der LaufzeitGröße von /dev/shm, 1 bis 8.192 MB.
healthcheckObjektkeinerEin Befehl, der sagt, ob der Service bereit ist.
dependsOnObjekt[][]Services, auf die dieser vor dem Start wartet.
egressStringnonenone oder public.
resourcesObjekt1 CPU, 1.024 MBCPU, Arbeitsspeicher und GPU.
platformStringkeinerlinux/amd64 oder linux/arm64.
oneshotBooleanfalseLäuft einmal durch, statt dauerhaft zu laufen.

Images und Builds

Pinne Images auf eine Version oder einen Digest. Ein Tag latest, main, master, nightly, edge oder dev oder gar kein Tag löst eine Warnung aus, weil ein Update sonst ändern könnte, was läuft. Image-Referenzen müssen der Registry-Grammatik folgen: keine Leerzeichen, Anführungszeichen oder Shell-Zeichen.

json
"build": {
  "context": "source",
  "dockerfile": "docker/Dockerfile",
  "target": "production",
  "args": { "NODE_ENV": "production" }
}
FeldTypStandardBeschreibung
context"source", "empty" oder HTTPS-Git-URL"source"source baut aus source.git. empty baut ein dockerfileInline, das keine Dateien kopiert, etwa ein eingefügtes Dockerfile.
dockerfilerelativer PfadkeinerPfad des Dockerfiles im Kontext.
dockerfileInlineStringkeinerEin komplettes Dockerfile, bis 32.000 Zeichen, für Projekte ohne eigenes. Entweder dies oder dockerfile.
targetStringkeinerBuild-Stage.
argsObjektkeinerBuild-Argumente. Werte dürfen Verweise enthalten.

Gebaut wird mit BuildKit auf dem Runner. Die Steuerungsebene klont und baut nie etwas.

Ports

json
"ports": [{ "container": 3000 }]
FeldTypStandardBeschreibung
containerIntegerPflichtPort im Container, 1 bis 65535.
protocolStringhttphttp, tcp oder udp.
nameSlugkeinerEine Bezeichnung für den Port.
publishBooleanfalseEinen rohen tcp- oder udp-Port auf dem Runner-Host veröffentlichen.
hostPortIntegerwählt der RunnerDie Host-Seite eines veröffentlichten Ports, etwa 53. Nur zusammen mit publish.

Von außen erreichbar ist nur der HTTP-Port, den expose nennt, und zwar über den Ingress-Proxy von Setup. Andere Ports erreichen nur die übrigen Services im privaten Netz der App.

Veröffentlichen ist für Homelab-Software gedacht, die etwas anderes als HTTP spricht, etwa DNS oder Gameserver. Ein Runner lehnt das ab, solange sein Betreiber --allow-published-ports nicht eingeschaltet hat (siehe Runner). HTTP-Ports lassen sich nicht veröffentlichen. Sie laufen immer über den Ingress.

Volumes

json
"volumes": [{ "name": "data", "path": "/app/data" }]

name ist ein Slug, path ein absoluter Pfad im Container ohne ... Volumes sind benannt, gehören zu genau einer App, überstehen Neustarts und Updates und sind Teil jeder Sicherung. Einen Ordner vom Runner-Host einzuhängen geht nicht.

Health-Checks

json
"healthcheck": {
  "command": ["curl", "-f", "http://127.0.0.1:3000/health"],
  "intervalSeconds": 10,
  "retries": 5
}
FeldTypStandardBeschreibung
commandString[]PflichtExec-Form, 1 bis 32 Teile, läuft im Container.
intervalSecondsInteger101 bis 300.
timeoutSecondsInteger51 bis 120.
retriesInteger51 bis 30.
startPeriodSecondsInteger300 bis 1.800. Fehlschläge in dieser Zeit zählen nicht.

Der Befehl muss im Image vorhanden sein. Die Rezepte im Katalog rufen 127.0.0.1 auf statt localhost. Hat ein Image einen eigenen HEALTHCHECK, wartet der Runner auch auf diesen.

Startreihenfolge und One-Shot-Services

json
"dependsOn": [{ "service": "db", "condition": "healthy" }]

condition ist started (Standard), healthy oder completed. healthy setzt einen healthcheck beim anderen Service voraus. completed wartet, bis ein One-Shot-Service fertig ist. Das passt für Datenbankmigrationen oder Downloads, die vorher laufen müssen. Zyklen sind ein Fehler.

Ein Service mit "oneshot": true läuft einmal durch. Er lässt sich nicht freigeben und zählt nicht zur CPU- und Speichersumme der App.

Internetzugang

Standardmäßig ("egress": "none") hat ein Service keinen Weg ins Internet oder ins lokale Netz. Er erreicht nur die anderen Services der App.

Mit "egress": "public" geht der Service über den Egress-Proxy der App ins Internet. Der Proxy blockt private, Loopback-, Link-Local- und Cloud-Metadaten-Adressen. Der Runner setzt dafür HTTP_PROXY und HTTPS_PROXY. Programme, die diese Variablen ignorieren, können nichts herunterladen. Die Rezeptprüfung warnt bei jedem Service mit öffentlichem Egress.

Ressourcen

FeldTypStandardBeschreibung
cpusZahl10,1 bis 16.
memoryMbInteger102464 bis 65.536.
gpuBooleankeinerDer Service braucht eine GPU. Nur Runner, die eine melden, können ihn ausführen.

Vor dem Deployment addiert Setup die dauerhaft laufenden Services und vergleicht die Summe mit den Grenzen deines Tarifs oder Runners. Geht einem Service der Speicher aus, sagt der Runner das und schlägt vor, memoryMb zu erhöhen.

Hinweis. Setup hostet keine Sprachmodelle. Braucht eine App ein Modell, sollte sie Adresse und Schlüssel einer OpenAI-kompatiblen API als Einstellungen nehmen.

Expose

json
"expose": { "service": "app", "port": 3000, "path": "/login" }
FeldTypStandardBeschreibung
serviceStringPflichtEin vorhandener Service, der kein One-Shot ist.
portIntegerPflichtMuss bei diesem Service als http-Port deklariert sein.
pathString/Wird nach dem Start im Browser geöffnet.

Checks

json
"checks": {
  "http": { "path": "/health", "expectStatus": [200], "contains": "ok" },
  "startupTimeoutSeconds": 300
}
FeldTypStandardBeschreibung
http.pathString/Wird über die öffentliche Adresse der App abgerufen.
http.expectStatusInteger[]jeder 2xx oder 3xxErlaubte Statuscodes, bis zu 20.
http.containsStringkeinerText, der in der Antwort vorkommen muss, bis 200 Zeichen.
startupTimeoutSecondsInteger30010 bis 1.800.

Ein Lauf besteht, wenn jeder dauerhafte Service läuft, jeder deklarierte Health-Check gesund ist und der HTTP-Check vor Ablauf der Zeit gelingt. Ohne checks.http ruft der Runner expose.path mit der Standardregel für Statuscodes ab. Weiterleitungen folgt er nicht. Eine Weiterleitung auf eine Login-Seite gilt also als bestanden, solange du kein expectStatus setzt. checks.http braucht expose.

Ein bestandener Lauf verifiziert das Rezept. Was das bewirkt, steht unter Maintainer.

Externe Dienste (requires)

Manche Apps brauchen etwas, das nicht in der Sandbox laufen kann: einen E-Mail-Anbieter, eine gehostete Datenbank, ein Zahlungskonto, einen eigenen OAuth-Client. requires nennt diese Dinge, damit sie sichtbar sind, bevor irgendetwas startet.

json
"requires": [
  {
    "id": "email",
    "kind": "api-credentials",
    "provider": "resend",
    "capability": "email-sending",
    "title": "Resend for email",
    "reason": "The app sends sign-in links and notifications by email.",
    "inputs": ["RESEND_API_KEY"],
    "docsUrl": "https://resend.com/docs"
  }
]
FeldTypStandardBeschreibung
idSlugPflichtEindeutig im Rezept.
kindStringPflichtmanaged-service, api-credentials, oauth-app oder deploy-target.
providerSlugPflichtZum Beispiel resend, cloudflare, convex, stripe.
capabilitySlugkeinerWas die App braucht, etwa email-sending, database, object-storage, backend, oauth-client, payments.
titleStringPflichtVerständlicher Name, bis 80 Zeichen.
reasonStringPflichtWarum die App es braucht, bis 500 Zeichen.
optionalBooleanfalseDie App läuft auch ohne, nur fehlt dann eine Funktion.
inputsString[][]Inputs, die diese Anforderung füllt, bis zu 16. Jeder muss in inputs deklariert sein, und nur eine Anforderung darf ihn füllen.
resourcesObjekt[]Standard des AnbietersWas Setup pro App anlegt, etwa { "type": "d1-database", "name": "db" }. Bis zu 8.
outputsObjektkeinerWelcher angelegte Wert welchen Input füllt. Die Schlüssel müssen in inputs stehen.
docsUrlURLkeinerWo man sich registriert oder die Zugangsdaten anlegt.
selfHostedObjektkeinercatalog (Slug eines Ersatzes, der in der Sandbox läuft) und note (bis 300 Zeichen).

Der Input selbst ist ein ganz normaler Input, den ein Service über einen Verweis nutzt:

json
"inputs": [
  {
    "key": "RESEND_API_KEY",
    "label": "Resend API key",
    "description": "Filled when you connect Resend, or enter it yourself.",
    "type": "secret"
  }
]

Beim Deployment listet das Dashboard jede Anforderung unter Externe Dienste auf und bietet Für mich anlegen, Eigene Werte verwenden, Von Hand einrichten und bei optionalen Anforderungen Ohne diesen Dienst starten an. Mit Für mich anlegen legt Setup in einem verbundenen Konto Ressourcen nur für diese eine App an und entfernt sie wieder, wenn du die App entfernst. Der Code dafür stammt aus Setups eigener, geprüfter Registry, nie aus dem analysierten Projekt. Welche Anbieter unterstützt werden, steht unter Integrationen und verbundene Konten.

resources und outputs zählen nur, wenn Setup etwas anlegt. Die Namen der Outputs folgen dem Modul des Anbieters, mit Bindestrichen. Inputs, die nicht in outputs stehen, ordnet Setup über bekannte Namen zu, etwa RESEND_API_KEY.

json
{
  "id": "database",
  "kind": "managed-service",
  "provider": "cloudflare",
  "capability": "database",
  "title": "Cloudflare D1 database",
  "reason": "The app keeps its data in a D1 database.",
  "inputs": ["D1_DATABASE_ID", "CLOUDFLARE_API_TOKEN"],
  "resources": [{ "type": "d1-database", "name": "db" }],
  "outputs": {
    "D1_DATABASE_ID": "db.database-id",
    "CLOUDFLARE_API_TOKEN": "api-token"
  }
}

Limits

WasGrenze
Services pro Rezept8
Inputs pro Rezept32
Anforderungen pro Rezept16
Tags12
Umgebungsvariablen pro Service128
Ports pro Service8
Volumes pro Service8
CPUs pro Service16
Arbeitsspeicher pro Service65.536 MB
Inline-Dockerfile32.000 Zeichen
Notes8.000 Zeichen
Start-Timeout1.800 Sekunden

Dein Tarif oder Runner kann für eine ganze App niedrigere Summen setzen.

Fehler und Warnungen

Setup prüft jedes Rezept zweimal: gegen das Schema und dann gegen Regeln, die das Schema nicht ausdrücken kann. Fehler verhindern das Deployment. Warnungen werden angezeigt und blockieren nichts.

Fehler sind unter anderem doppelte Input-Schlüssel, ein select ohne options, unbekannte Verweise, ein Service, der von sich selbst abhängt, eine healthy-Abhängigkeit auf einen Service ohne Health-Check, Abhängigkeitszyklen, ein Build aus dem Quellcode ohne source.git, doppelte Ports oder Volume-Pfade, hostPort ohne publish, ein veröffentlichter HTTP-Port, ein expose, das auf einen fehlenden Service oder einen nicht deklarierten Port zeigt, und checks.http ohne expose.

Warnungen gibt es für nicht gepinnte Image-Tags, veröffentlichte Ports, öffentlichen Egress und jede Anforderung, die nicht optional ist.

Was ein Rezept nicht ausdrücken kann

Das Format hat kein Feld für:

  • Ordner auf dem Runner-Host oder Bind-Mounts,
  • privileged oder Linux-Capabilities,
  • Geräte, abgesehen vom gpu-Flag,
  • Host-Netzwerk oder die PID- und IPC-Namespaces des Hosts,
  • Sysctls, Kernel-Tuning oder Security-Optionen,
  • den Docker-Socket.

Unbekannte Schlüssel verwirft Setup beim Einlesen. Schreibst du "privileged": true in einen Service, gibt es keinen Fehler, es passiert schlicht nichts. Container-Pfade müssen absolut sein, Build-Pfade relativ, und keiner darf .. enthalten.

Der Grund: Setup führt Software aus, die Fremde geschrieben haben, oft auf einem Rechner, den sich mehrere Apps teilen. Jede dieser Optionen würde einem Container einen Weg zum Host oder zu seinen Nachbarn öffnen. Ein Rezept, das sie nicht ausdrücken kann, lässt sich auch nicht dazu überreden. Runner legen ihre eigene Härtung obendrauf: entzogene Capabilities, ein privates Netz pro App, Grenzen für CPU, Speicher und Prozesse. Die einzige Ausnahme zum Einschalten ist publish für rohe Ports, und auch nur auf Runnern, deren Betreiber es erlaubt. Das ganze Modell steht unter Sicherheit.