Entender o que roda onde deixa todas as outras decisões operacionais — escala, backup, política de rede, upgrades — muito mais simples.
Serviços
| Serviço | Imagem | Porta | Sem estado | Obrigatório |
|---|---|---|---|---|
| app | ghcr.io/seeyuai/studio | 3000 | Somente com object storage configurado | Sim |
| realtime | ghcr.io/seeyuai/realtime | 3002 | Sim | Sim |
| migrations | ghcr.io/seeyuai/migrations | — | Sim (roda uma vez) | Sim |
| postgresql | pgvector/pgvector:pg17 | 5432 | Não | Sim |
| redis | redis:7-alpine | 6379 | Em grande parte | Incluído nos dois; troque por uma instância gerenciada em produção |
| cron | ghcr.io/seeyuai/cron (Compose) / curlimages/curl (CronJobs) | — | Sim | Sim |
| pii | ghcr.io/seeyuai/pii | 5001 | Sim | Opcional |
| ollama | ollama/ollama | 11434 | Não (cache de modelos) | Opcional |
| telemetry | otel/opentelemetry-collector-contrib | 4317/4318 | Sim | Opcional |
app
A aplicação Next.js: a interface do editor, todas as rotas de API e o motor de execução de workflows. Por padrão, as execuções de workflow acontecem dentro do processo do app, usando um sandbox isolated-vm — é por isso que o recurso limitante é a memória, e não a CPU. Tanto o chart quanto o arquivo compose solicitam 4 Gi e limitam o app a 8 Gi. Configurar um provedor de sandbox remoto (E2B ou Daytona) tira a execução de código do processo; veja Segurança.
Qualquer réplica pode atender qualquer requisição desde que o object storage esteja configurado. Até então, o app grava os uploads no sistema de arquivos do próprio contêiner, o que o torna stateful — veja Onde o estado fica. Escale horizontalmente só depois de ler Escala e HA, por causa dos pré-requisitos de Redis, storage e pool de conexões.
realtime
Um servidor Socket.IO em Bun que cuida da edição colaborativa, das atualizações de execução ao vivo e dos documentos colaborativos. Os clientes se conectam em /socket.io.
Ele compartilha o banco de dados e o BETTER_AUTH_SECRET com o app (o padrão de sessão compartilhada em banco do Better Auth), então autentica os mesmos usuários sem um login separado.
Escalar o realtime para além de uma réplica exige REDIS_URL — é o adaptador Redis do Socket.IO que leva os eventos entre os pods. Sem ele, dois usuários em pods diferentes simplesmente param de ver as edições um do outro.
migrations
Aplica as migrações de schema do Drizzle e encerra. No Docker Compose é um serviço de execução única; no Kubernetes é um init container no Deployment do app, então as migrações rodam antes de qualquer pod do app ficar pronto e voltam a rodar (sem efeito) em cada rollout.
As migrações são somente para frente. Veja Upgrades.
postgresql
PostgreSQL 17 com a extensão pgvector, que é obrigatória — os embeddings da base de conhecimento são armazenados e pesquisados como vetores. A imagem pgvector/pgvector:pg17 já a inclui; uma instância gerenciada precisa ter a extensão habilitada (as migrações do Studio executam CREATE EXTENSION automaticamente quando as permissões permitem).
É aqui que fica praticamente todo o estado durável: workflows, execuções, logs, usuários, organizações, credenciais, chunks e embeddings da base de conhecimento, e os dados das tabelas.
redis
Dá suporte a pub/sub, ao adaptador do Socket.IO, ao armazenamento de idempotência, aos marcadores de progresso de execução, aos limites distribuídos de execução e ao armazenamento de aprovações da autenticação por CLI. Os usos parecidos com armazenamento recorrem ao Postgres ou a estado em processo. O pub/sub recorre a um emissor local ao processo, o que funciona bem com uma réplica e descarta todos os eventos entre pods quando há mais de uma. Veja Redis.
cron
Dezoito jobs agendados que chamam endpoints internos — execução de agendamentos, triggers de polling, renovação de assinaturas de webhook, syncs de conectores, processamento do outbox, drenagem de dados e limpeza de imagens de sandbox. O Kubernetes os executa como CronJobs; o Docker Compose os executa a partir de um único serviço supercronic. Mesmos caminhos, mesmos agendamentos. Veja Jobs em segundo plano.
Onde o estado fica
Três lugares, uma vez que a implantação esteja configurada para produção. Todo o resto é descartável.
| Armazenamento | Conteúdo | Backup |
|---|---|---|
| PostgreSQL | Todos os dados da aplicação | pg_dump / snapshots gerenciados + PITR |
| Object storage | Arquivos enviados, documentos da base de conhecimento, saídas de execução, avatares, logotipos | Versionamento de bucket + ciclo de vida |
| Secrets | ENCRYPTION_KEY, API_ENCRYPTION_KEY, BETTER_AUTH_SECRET, INTERNAL_API_SECRET, CRON_SECRET | Gerenciador de secrets |
O object storage não vem configurado por padrão, e o fallback não é durável. O Studio só usa S3, Azure Blob ou GCS quando as variáveis correspondentes estão definidas (S3_BUCKET_NAME + AWS_REGION, AZURE_STORAGE_CONTAINER_NAME + credenciais, ou GCS_BUCKET_NAME). Sem nenhuma delas, ele grava os uploads em um diretório dentro do contêiner do app — e nem o docker-compose.prod.yml nem o Helm chart montam um volume ali. Os arquivos são perdidos quando o contêiner é recriado e ficam invisíveis para as outras réplicas. Configure o object storage antes de armazenar qualquer coisa que importe, e antes de passar de uma réplica.
A ENCRYPTION_KEY não é recuperável nem derivável. Ela criptografa as variáveis de ambiente do workspace e pessoais, as chaves de API de provedores armazenadas, as credenciais OAuth de MCP e os secrets de deploy e de chat em repouso — restaurar o banco de dados com uma chave diferente resulta em uma aplicação funcional em que nada disso pode ser descriptografado. Faça o backup dela separadamente do banco de dados, e nunca a rotacione sem necessidade.
O Redis é um cache e um barramento de mensagens. Perdê-lo derruba as atualizações ao vivo em andamento; não perde dados já confirmados.
Caminhos das requisições
Editor / API — navegador → ingress/proxy reverso → app:3000 → Postgres, Redis, object storage.
Colaboração — navegador → ingress → realtime:3002 (/socket.io, upgrade para WebSocket) → pub/sub do Redis → outros pods de realtime. O proxy precisa repassar os cabeçalhos de upgrade e permitir conexões ociosas de longa duração; veja Rede.
Upload de arquivo (com object storage configurado) — o navegador pede ao app para abrir uma sessão de upload → o app devolve instruções de transferência assinadas → o navegador envia os bytes diretamente ao object storage (um PUT de até 50 MB, partes multipart acima disso) → o navegador avisa ao app que a sessão terminou e o app registra os metadados. É por isso que os buckets precisam de uma política de CORS nomeando a origem do seu Studio. Os downloads são feitos por proxy através do app.
Upload de arquivo (disco local) — a mesma sessão de upload é aberta, mas as instruções de transferência apontam de volta para os endpoints /api/v2/uploads/... do próprio app, em vez de um bucket, então os bytes passam pelo app. Não há configuração de CORS envolvida, e nenhum bucket é usado.
Execução de workflow — trigger (manual, API, webhook ou agendamento) → o app enfileira ou executa inline → sandbox isolated-vm → resultados e logs no Postgres, marcadores de progresso no Redis.
Trabalho em segundo plano — CronJob → Authorization: Bearer $CRON_SECRET → endpoint do app → mesmo caminho de execução.
Fronteiras de rede
| De | Para | Finalidade |
|---|---|---|
| Internet | app:3000, realtime:3002 | Usuários |
| app, realtime | postgresql:5432 | Dados |
| app, realtime | redis:6379 | Pub/sub, cache |
| app | Endpoint do object storage | Arquivos (lado servidor) |
| Navegador | Endpoint do object storage | Uploads pré-assinados — precisa ser acessível publicamente |
| app | APIs de provedores de modelo, APIs de integrações, provedor de SMTP/e-mail | Saída |
| cron | app:3000 (Service interno / rede do compose) | Triggers agendados |
A NetworkPolicy opcional do chart bloqueia por padrão os endpoints de metadados da nuvem (169.254.169.254), mas permite ingress de qualquer pod do cluster, a menos que você restrinja networkPolicy.ingressFrom. Veja Segurança.