Auf einem Server deployen¶
Ein Host, Docker Compose, ein Reverse Proxy davor. Das ist der gesamte ausgelieferte Weg, und genau er läuft in den Deployments, in denen dieses Projekt eingesetzt wird.
Es gibt keine Kubernetes-Manifeste und keine Ein-Klick-Schnellstarts für die
Platform-as-a-Service-Anbieter. Das ist keine Bescheidenheit in Bezug auf die
Skalierung. Der Stack besteht aus sechs Containern, zwei davon halten Zustand,
einer kann eigene Container starten, und einer ist ein Postgres, das pgvector
haben muss - was bereits mehr ist, als ein git push-Deploy-Ziel abbildet, und
eine Anleitung, die etwas anderes vorgäbe, würde ein Deployment beschreiben, das
niemand betrieben hat.
Lesen Sie zuerst die Produktions-Checkliste
Neun Einstellungen werden mit Standardwerten ausgeliefert, die auf einem Laptop
in Ordnung und auf einem Host, den jemand anderes erreichen kann, falsch sind.
scripts/server-init.sh weiter unten erzeugt alle neun, die Checkliste ist also
das, was Sie danach prüfen, und nicht das, was Sie tippen.
Was Sie brauchen¶
| Einen Host | 4 vCPU und 8 GB RAM genügen. Siehe Dimensionierung |
| Docker | Engine 24+ mit dem Compose-Plugin (2.24 oder neuer), und Ihr Benutzer in der Gruppe docker. Auf dem Host wird nichts gebaut: die Images werden von GHCR geholt |
| Zwei Hostnamen | einen für die Website, einen für die API — siehe warum zwei |
| Einen Reverse Proxy | Traefik oder Nginx. Er terminiert TLS |
| Keinen Provider-Key | Chat-Modelle und Embeds werden beide pro Organisation im Vault des Produkts hinterlegt. Die Umgebung hält für keines von beiden Zugangsdaten — siehe Datenschutz |
Der Host braucht außerdem die Ports 80 und 443 offen, und sonst nichts. Postgres, Redis und die Prefect-API sind auf keiner Schnittstelle veröffentlicht.
Warum zwei Hostnamen¶
Der Browser spricht mit beiden. Die meisten Aufrufe laufen über die serverseitigen Routen des Frontends, aber der Chat-WebSocket verbindet sich direkt mit der API, also braucht die API einen Namen, den ein Browser auflösen kann, und ein eigenes Zertifikat.
app.example.com und api.example.com ist die Form. Es können zwei beliebige
Namen sein; was sie nicht sein dürfen, ist ein Name mit einem Pfadpräfix, denn
die Cookies der API und die der Website sind auf den Host begrenzt.
Den Host dimensionieren¶
Auf einem im Leerlauf befindlichen Deployment gemessen, nicht geschätzt:
| im Ruhezustand | Obergrenze | |
|---|---|---|
app (2 uvicorn workers) |
~1,0 GB | 2,5 GB bei den standardmäßigen 4 Workern |
db |
~1,3 GB mit dem Tuning unten | 2 GB |
prefect-runner |
241 MiB | 1,5 GB |
prefect-server |
245 MiB | 768 MB |
frontend |
~300 MB | 1 GB |
redis |
9 MiB | 512 MB |
Die Zahl, die über den Host entscheidet, ist UVICORN_WORKERS. Jeder Worker
ist ein eigener Prozess, der die gesamte Anwendung importiert — 460 MiB, per
Spawn statt per Fork erzeugt, es wird also nichts geteilt. Vier davon sind 1,9 GB,
bevor eine Anfrage eintrifft.
Zwei Worker passen zu einem Team von zehn Personen und lassen immer noch einen bedienen, während der Watchdog ein festgefahrenes Geschwister ersetzt. Ein Worker ist die Einstellung, die es zu vermeiden gilt: eine blockierte Event Loop ist dann das gesamte Deployment, bis es sich selbst beendet.
Die Datenbank ist auf ihr eigenes Limit hin abgestimmt
docker-compose-prod.yml betreibt Postgres mit shared_buffers=512MB gegen ein
Limit von 2 GB und gibt ihm 512 MB /dev/shm — Dockers Standard sind 64 MB, die
ein paralleler Scan über die Vektoren einer Collection erschöpft, mit der Meldung
could not resize shared memory segment. Verschieben Sie das Limit, verschieben
Sie das Tuning mit; sie stehen aus diesem Grund nebeneinander.
Die Namen auf den Host zeigen lassen¶
Zwei A-Records, vor allem anderen. Let's Encrypt weist nach, dass Sie einen Namen kontrollieren, indem es eine Datei über HTTP von dort abruft, wohin der Name auflöst; ein Zertifikat wird also erst ausgestellt, wenn das zutrifft und propagiert ist.
Ein Wildcard erledigt das nicht für Sie
Wo *.example.com bereits irgendwohin zeigt — meist auf eine Marketing-Website —
lösen beide Namen dorthin auf. Ein Record für den konkreten Namen schlägt einen
Wildcard, die Lösung besteht also darin, die beiden oben hinzuzufügen, und nicht
darin, den Wildcard zu entfernen.
Prüfen Sie von einem Ort, der nicht der Host ist, denn der Host kann eine eigene Antwort haben:
Es auf den Host bringen¶
sudo install -d -o "$USER" -g "$USER" /opt/agenticos
git clone https://github.com/vstorm-co/agenticos.git /opt/agenticos
cd /opt/agenticos
bash scripts/server-init.sh
server-init.sh schreibt backend/.env: es erzeugt die fünf Secrets, fragt nach
den beiden Hostnamen und nach einer Adresse für Let's Encrypt und leitet die
öffentlichen URLs und den CORS-Origin aus Ihren Angaben ab. Nach einem
Provider-Key fragt es nicht: die liegen im Vault der jeweiligen Organisation.
Eine bestehende Datei überschreibt es nicht.
Der Klon ist der Ort, an dem die Compose-Dateien und diese env-Datei liegen; von dort läuft kein Code. Was läuft, sind die beiden Images, die das Repository veröffentlicht.
Die Images¶
ghcr.io/vstorm-co/agenticos-backend |
Die API, der Prefect-Runner und die Migrationen - ein Image, drei Kommandos |
ghcr.io/vstorm-co/agenticos-frontend |
Die Konsole |
Beide werden von .github/workflows/images.yml für linux/amd64 und
linux/arm64 gebaut. Ein Release (v0.0.380) veröffentlicht 0.0.380 und
verschiebt latest; jeder Commit auf main veröffentlicht edge und
sha-<short>. Die Compose-Dateien lesen den Tag aus AGENTICOS_VERSION in
backend/.env und fallen auf latest zurück.
Drei Regeln, die der Workflow einhält, jede davon wissenswert, bevor Sie sich auf einen Tag verlassen:
- Nur ein Commit auf
mainwird jemals veröffentlicht. Einv*-Tag, der von einem Branch aus gesetzt wird, oder ein dort gestarteter Lauf, wird abgelehnt, bevor irgendetwas gebaut wird -latestkann also nicht über die Grenze des Pull Requests hinausgelangen. - Ein Release auf einem Commit, den
mainbereits gebaut hat, wird nicht neu gebaut. Seinsha-<short>-Manifest erhält die Version undlatestals zusätzliche Namen, damit die Digests, auf die ein Host gepinnt ist, genau die sind, die das Release benennt. - Ein Commit ohne Images kann sie bekommen. Starten Sie den Workflow von Hand
mit seiner
sha-Eingabe -gh workflow run images.yml --ref main -f sha=<commit>- und er veröffentlicht densha-<short>-Tag dieses Commits und nichts, was sich bewegt. Das ist der Weg für einen Commit, der älter ist als der Workflow, und für einen, dessen Lauf verloren ging.
Pinnen Sie ein Release auf einem Host, der Ihnen wichtig ist
AGENTICOS_VERSION=0.0.380 in backend/.env, damit make prod an einem
schlechten Tag das holt, was gestern lief, und nicht das, was heute Morgen
veröffentlicht wurde. scripts/deploy.sh pinnt für Sie - auf den sha--Tag des
Commits, den es deployt - für genau so lange, wie der Deploy läuft.
Beide Pakete lassen sich anonym holen. Antwortet ein Pull mit unauthorized,
wurde das Paket auf privat gestellt oder ein veraltetes docker login ghcr.io
steht im Weg; keines von beidem kann ein Host von sich aus beheben.
backend/.env hält den Schlüssel, der jede gespeicherte Zugangsinformation entpackt
VAULT_MASTER_KEY ist das, was die Provider-Keys, Bot-Token und
MCP-Zugangsdaten einer Organisation lesbar macht. Ihn zu verlieren sperrt Sie
nicht aus dem Produkt aus; es macht jedes Secret darin unwiederbringlich. Sichern
Sie die Datei an einem Ort, den eine verlorene Festplatte nicht mitnimmt, und
rotieren Sie mit agenticos cmd vault-rotate statt
durch Editieren.
Zwei optionale Dinge, nach denen es nicht fragt, beide in dieser Datei: SMTP_*,
ohne das sich Einladungen und Passwort-Resets nicht versenden lassen, und
LOGFIRE_TOKEN, wohin die Traces der Agent-Runs gehen.
Einen Reverse Proxy wählen¶
Irgendetwas muss TLS terminieren und die beiden Namen routen. Beide Optionen unten erreichen dieselben Container; wählen Sie danach, ob Sie bereits einen betreiben.
Option A: Traefik¶
Der kürzere Weg, und der, den man auf einem Host mit bereits vorhandenem Traefik wählt: die Container tragen Labels, Traefik entdeckt sie, fordert das Zertifikat an und erneuert es. Nichts neu zu laden und keine zweite Konfigurationsdatei, die im Gleichschritt bleiben muss.
Ist Traefik noch nicht vorhanden, liefert das Repository einen mit:
traefik/traefik.yml und docker-compose-traefik.yml, das ist ein Entrypoint auf
443 mit einem Let's-Encrypt-Resolver und 80, das dorthin umleitet.
docker network create traefik_webgateway
docker compose --env-file backend/.env -f docker-compose-traefik.yml up -d
Wo Traefik bereits läuft, lassen Sie diese Dateien in Ruhe und zeigen mit
TRAEFIK_NETWORK auf das Netzwerk, das er beobachtet. Die Overlays lesen diesen
Namen, am bestehenden Proxy muss sich also nichts ändern.
Bringen Sie den Stack dann mit PROXY=traefik hoch, was die beiden
Overlay-Dateien hinzufügt, die die Labels tragen:
server-init.sh hat PROXY=traefik bereits in backend/.env geschrieben, und
von dort liest scripts/deploy.sh es — spätere Deploys behalten also den Proxy
bei, mit dem dieser Host eingerichtet wurde, und nicht den, den ein Skript
annahm.
exposedByDefault: false leistet echte Arbeit
Es ist die eine Einstellung in traefik/traefik.yml, die es sich zu lesen lohnt,
bevor Sie ihn betreiben. Nur app und frontend tragen traefik.enable=true,
also sind Postgres, Redis, der Prefect-Server und der Sandbox-Daemon von nichts
außerhalb des Hosts erreichbar — und das ist eine Eigenschaft davon, nicht
gelabelt zu sein, sie überlebt also, dass jemand einen Dienst hinzufügt, ohne an
den Proxy zu denken.
Option B: Nginx¶
Für einen Host, auf dem Nginx bereits TLS terminiert, oder auf dem der Proxy gar
nicht in Docker läuft. Der Stack veröffentlicht beide Ports auf 127.0.0.1, und
Nginx erreicht sie dort:
nginx/nginx.conf ist die Vorlage. Zwei Ersetzungen, bevor sie irgendetwas
ausliefert: der server_name in jedem Block ist ${DOMAIN:-localhost}, und
Nginx expandiert das nicht — tragen Sie die beiden Hostnamen von Hand ein.
Zertifikate zu beschaffen und zu erneuern ist Ihre Sache, und ebenso der Header
Strict-Transport-Security, den das Backend bewusst dem überlässt, was TLS
terminiert.
BIND_HOST ist eine Sicherheitseinstellung, keine Bequemlichkeit
Der Loopback-Standard ist das, was das Auth-Rate-Limit überhaupt bedeutsam macht.
RATE_LIMIT_TRUST_FORWARDED_FOR weist die API an, einen Versuch der Adresse
anzurechnen, die der Proxy weiterreicht — was immer die API also am Proxy
vorbei erreichen kann, wählt die Adresse, der seine Versuche angerechnet
werden. Setzen Sie BIND_HOST=0.0.0.0 nur für einen Proxy auf einer anderen
Maschine, und schirmen Sie den Port per Firewall auf ihn ein.
Das Frontend ist ein eigenes Compose-Projekt
Compose benennt ein Projekt nach dem Verzeichnis, beide Stacks hießen also
agenticos — und das Hochfahren des Frontends meldete dann die fünf
Backend-Container als Waisen, mit Composes eigenem Vorschlag, das Kommando
erneut mit --remove-orphans auszuführen. Diesem Rat zu folgen stoppt die API,
die Datenbank, Redis und beide Prefect-Dienste. Die make-Targets und
scripts/deploy.sh übergeben -p agenticos-frontend, die Warnung ist damit weg.
Die Compose-Dateien legen auch keine Containernamen fest - jedes Projekt benennt
seine eigenen, zwei Stacks auf einem Host können einander also nicht die
Container wegnehmen, und deploy.sh wartet auf die Dienste app und frontend
statt auf einen Namen. Ein Deployment, das älter ist als beide Korrekturen, wird
bei seinem nächsten up unter den neuen Namen neu erstellt; von Hand muss nichts
entfernt werden.
Die beiden Projekte treffen sich weiterhin auf einem Netzwerk mit festem Namen -
agenticos_edge in Produktion, agenticos_backend auf dem Dev-Server - weil das
Frontend ihm als externem Netzwerk beitritt. Ein Host, auf dem zwei
AgenticOS-Stacks laufen, setzt AGENTICOS_EDGE_NETWORK und
AGENTICOS_DATA_NETWORK (oder AGENTICOS_NETWORK) in der backend/.env jedes
Stacks unterschiedlich; sonst lösen db, redis und app beider Stacks auf
einer Bridge auf, und eine Anfrage kann die Datenbank des Nachbarn erreichen.
Starten und den ersten Zugang anlegen¶
make prod holt die Images, startet den Stack und führt die Migrationen aus -
letztere als migrate-Dienst, auf den die API wartet, ein docker compose up -d
von Hand auf denselben Dateien tut also dasselbe. Der erste Pull umfasst rund
2 GB; ein späterer sind die Layer, die sich geändert haben.
Legen Sie dann eine Organisation, einen Owner und einen funktionierenden Agent an:
docker compose --env-file backend/.env -f docker-compose-prod.yml \
exec -T app agenticos cmd bootstrap \
--email you@example.com --password 'a real password' \
--org 'Your Company' --provider anthropic --api-key sk-ant-...
Der Provider-Key an dieser Stelle ist das, worauf der Demo-Agent läuft. Ohne ihn wird der Agent angelegt und kann nicht antworten; jeder andere Provider wird im Produkt hinzugefügt, pro Organisation, aus dem Vault.
Prüfen Sie es von außen, nicht vom Host aus
curl -fsS https://api.example.com/api/v1/health
curl -fsSo /dev/null -w '%{http_code}\n' https://app.example.com
Ein Stack, der auf dem Host gesund und aus dem Internet unerreichbar ist, ist DNS, die Firewall oder das Zertifikat — drei Dinge, die ein Health Check innerhalb des Hosts nicht sehen kann.
Dann, einmal, von Hand: melden Sie sich an, laden Sie jemanden ein (was SMTP_*
nachweist) und schicken Sie dem Demo-Agent eine Nachricht (was den Provider-Key
und den WebSocket nachweist). Jedes davon übt einen Pfad aus, den hier sonst
nichts prüft.
Die Security-Header kommen aus dem Backend, jeder Proxy ist damit abgedeckt
Eine Content-Security-Policy, X-Frame-Options: DENY,
X-Content-Type-Options: nosniff, Referrer-Policy und Permissions-Policy
werden auf jeder Antwort gesetzt — einschließlich der 500 für eine unbehandelte
Exception, die außerhalb des Middleware-Stacks gebaut wird und sie sich selbst
aufprägt. Nur die interaktive API-Dokumentation verzichtet auf die CSP, weil
Swagger Assets lädt, die eine strenge Policy verbietet.
HSTS ist bewusst dem Proxy überlassen, denn dort terminiert TLS. Ein Proxy, der eine eigene CSP setzt, sollte mindestens so streng sein wie diese.
Die Sandbox einschalten¶
Der Dienst, der den Code eines Agents ausführt, liegt hinter einem Compose-Profil, denn er ist der eine Container, der den Docker-Socket hält, und den auf einem gemeinsam genutzten Host einzuhängen sollte eine Entscheidung sein statt eine Voreinstellung. Drei Dinge, einmalig:
make sandbox-token # writes SANDBOXD_TOKEN to backend/.env
sudo mkdir -p /var/lib/agenticos/sandbox-workspaces
sudo chown 10001:10001 /var/lib/agenticos/sandbox-workspaces
Ein Deploy bringt sie dann hoch: scripts/deploy.sh übergibt --profile sandbox,
wenn SANDBOXD_TOKEN in backend/.env einen Wert hat, der Host selbst sagt also,
ob er eine betreibt. Es exportiert außerdem DOCKER_GID, vom Socket gelesen —
jede Compose-Datei hier interpoliert es in das group_add der Sandbox, und sein
Standardwert 0 ist auf so gut wie keiner Linux-Distribution der Eigentümer des
Sockets. Sonst wird nichts in .env gebraucht: das Backend erreicht den Daemon
über eine Sandbox-Connection, die jemand in der Konsole anlegt, und
http://sandboxd:8080 wird als die eigene dieses Deployments erkannt.
Ein Profil, von dem Compose nichts weiß, ist ein Dienst, den Compose stoppt
Ein up -d auf demselben Projekt ohne --profile sandbox lässt die Sandbox nicht
in Ruhe — es stoppt sie. Einem Host, der sie von Hand gestartet hatte, wurde sie
also vom nächsten Deploy weggenommen, wobei die Codeausführung eines Agents aus
Gründen fehlschlug, die weit weg vom auslösenden Deploy lagen (#1506). Deshalb
liest das Skript den Token, statt ein Flag entgegenzunehmen.
Eine Änderung deployen¶
Von Hand¶
remote=$(ssh you@your-host 'mktemp -t agenticos-deploy.XXXXXX')
ssh you@your-host "cat > $remote" < scripts/deploy.sh
ssh you@your-host "trap 'rm -f $remote' EXIT; bash $remote <commit-sha>"
scripts/deploy.sh holt diesen Commit, wartet auf die Images, die die CI dafür
veröffentlicht hat (sha-<short>, meist schon da), holt sie, startet neu und
wartet darauf, dass beide Container sich als gesund melden, bevor es mit einem
Wert ungleich null zurückkehrt oder eben nicht. Es nimmt einen Commit statt
eines Branches entgegen, das Deployte ist also das Geprüfte und nicht das, wohin
main seither gewandert ist - und was läuft, ist Byte für Byte das, was die CI
gebaut hat, auf einem Host, der die Toolchain nie braucht.
Kopieren Sie es auf den Host und führen Sie es dann aus — leiten Sie es nicht in bash -s
Unter bash -s ist das Skript die Standardeingabe der Shell selbst, und das erste
Kommando darin, das stdin liest, verbraucht den Rest. docker compose exec
reicht stdin auch mit -T an den Container weiter, die Migration hat also alles
unter sich aufgefressen, bash erreichte EOF, und der Deploy endete mit 0,
ohne jemals das Frontend gebaut oder auf irgendeinen Container gewartet zu haben.
Die Website war unten und der Deploy war grün
(#1488).
Zwei Verbindungen statt einer sind das, was der Prozedur die Fähigkeit nimmt, sich selbst abzuschneiden.
Es ist nicht ausfallfrei. Compose erstellt die Container neu, deren Image sich geändert hat, die Website ist also für die wenigen Sekunden, die das dauert, nicht verfügbar.
Aus GitHub, mit einer Freigabe¶
.github/workflows/deploy.yml bietet jeden Merge auf main zum Deployment an
und wartet darauf, dass jemand ihn freigibt. Dieses Tor ist eine
Repository-Einstellung, kein Schritt in der Datei — ohne es deployt der
Workflow jeden Merge unbeaufsichtigt.
Einmal einrichten:
- Settings → Environments → New environment, benannt
production. - Required reviewers ankreuzen und hinzufügen, wer freigeben darf. Das ist das Tor.
- Die Variablen der Environment hinzufügen:
SITE_URL,API_URLundAPP_DIR, falls der Checkout nicht unter/opt/agenticosliegt. - Die Secrets unten hinzufügen.
Brechen Sie einen Deploy ab, den Sie nicht freigeben wollen
Jeder Lauf teilt sich die Concurrency-Gruppe deploy-production, und ein Lauf,
der am Freigabe-Tor sitzt, hält sie. Er läuft nicht von selbst ab — GitHub bricht
einen unbearbeiteten nach 30 Tagen ab — bis also jemand ihn freigibt oder
abbricht, stauen sich spätere Merges hinter einer Entscheidung, die niemand
treffen wird, und der Server betreibt weiter das, was zuletzt deployt wurde.
Ein Deploy, gegen den Sie sich entschieden haben, wird also abgebrochen und nicht stehen gelassen. Einer, der auf einem überholten Commit wartete, hat hier drei spätere Läufe blockiert, bevor jemand die Warteschlange statt der Läufe bemerkte.
| Secret | Was |
|---|---|
DEPLOY_HOST |
Die Adresse des Hosts |
DEPLOY_USER |
Das Konto, dem der Checkout gehört |
DEPLOY_SSH_KEY |
Ein privater Schlüssel, dessen öffentliche Hälfte in den authorized_keys dieses Kontos liegt |
DEPLOY_KNOWN_HOSTS |
ssh-keyscan your-host, ausgeführt von einem Ort, dem Sie vertrauen |
Erzeugen Sie den Schlüssel dafür und für nichts sonst:
ssh-keygen -t ed25519 -N '' -C 'github-actions-deploy' -f deploy_key
ssh-copy-id -f -i deploy_key.pub you@your-host
ssh-keyscan your-host # → DEPLOY_KNOWN_HOSTS
cat deploy_key # → DEPLOY_SSH_KEY, then delete it locally
Der Host-Key ist ein Secret und kein ssh-keyscan zur Deploy-Zeit
Zur Deploy-Zeit zu scannen vertraut allem, was unter dieser Adresse antwortet, und genau das ist es, was ein Host-Key verhindern soll. Scannen Sie einmal, von einem Ort, dem Sie vertrauen, und speichern Sie die Antwort.
Es erscheint dann ein Lauf mit Review deployments; ihn freizugeben startet den
Job. workflow_dispatch führt denselben Job gegen eine von Ihnen benannte Ref
aus, so wird ein Rollback gemacht, und er durchläuft dieselbe Freigabe.
Backups¶
Ein Volume zählt, und welches, ist nicht offensichtlich:
| Volume | Enthält | Backup |
|---|---|---|
postgres_data |
alles — Agents, Unterhaltungen, versiegelte Zugangsdaten | ja |
media_data |
hochgeladene Dateien, vor der Ingestion | ja |
redis_data |
Rate-Limit-Buckets und Caches | nein, alles wiederherstellbar |
prefect_data |
die Historie der Flow-Runs | nein |
docker compose --env-file backend/.env -f docker-compose-prod.yml exec -T db \
sh -c 'pg_dump -U "$POSTGRES_USER" -Fc "$POSTGRES_DB"' > "agenticos-$(date +%F).dump"
Die Bezeichner kommen aus der Umgebung des Containers selbst, statt ausgeschrieben zu werden, denn beide sind Einstellungen: ein Deployment, das eine davon geändert hat, bekäme sonst eine leere Datei und einen Fehler, den im Vorbeigehen niemand liest.
Ein Datenbank-Backup ohne backend/.env ist kein Backup
Die Zugangsdaten darin sind mit VAULT_MASTER_KEY versiegelt. Neben einem anderen
Schlüssel wiederhergestellt, ist jeder Provider-Key, jeder Bot-Token und jede
MCP-Zugangsinformation im Dump unlesbar — und das Produkt wird Ihnen das eine
Ablehnung nach der anderen mitteilen.
Zurückrollen¶
| Wie | |
|---|---|
| Code | Den vorherigen Commit deployen: workflow_dispatch mit seinem SHA, oder scripts/deploy.sh. Die Images liegen noch in der Registry, das ist also ein Pull und kein Build. Ein Commit ohne sha-<short>-Images - älter als images.yml, oder sein Lauf ging verloren - wird zuerst mit gh workflow run images.yml --ref main -f sha=<commit> veröffentlicht; der Deploy nennt dieses Kommando, wenn er das Warten aufgibt |
| Schema | agenticos db downgrade --revision=-1, dann den passenden Code deployen |
| Daten | Den Dump per pg_restore einspielen, dann die Migration prüfen, die der Code erwartet |
Code über eine Migration hinweg zurückzurollen ist eine Entscheidung, kein Kommando. Der alte Code trifft auf ein Schema, das er nie gesehen hat; ob das funktioniert, hängt von der Migration ab. Lesen Sie sie, bevor Sie etwas annehmen.
Zusammenfassung¶
- Ein Host, Compose, ein Proxy davor. Sieben Container - einer davon führt
die Migrationen aus und beendet sich - zwei davon zustandsbehaftet, jeder
einzelne geholt - auf dem Host wird nichts gebaut. Pinnen Sie
AGENTICOS_VERSION. UVICORN_WORKERSentscheidet, was der Host kostet. 460 MiB pro Worker, nichts geteilt. Zwei für ein Team, vier für echten Verkehr.- DNS vor allem anderen. Es wird kein Zertifikat ausgestellt, bis die Namen auf den Host auflösen.
- Die Freigabe ist eine Repository-Einstellung, keine Zeile im Workflow. Ohne
Required Reviewers auf der Environment
productiondeployt sich jeder Merge selbst. - Sichern Sie
postgres_dataundbackend/.envzusammen. Das eine ohne das andere ist keine Wiederherstellung.