Como instalar o Typebot em uma VPS

Atualizado em 21/09/2026 30 min Intermediário

O Typebot monta conversas em formato de fluxo — o visitante responde uma pergunta por vez, e o resultado vai para onde você mandar. Serve de formulário de captação, qualificação de lead ou atendimento de primeiro nível.

A instalação tem uma particularidade: são duas aplicações. O builder é onde você desenha os fluxos; o viewer é o que o visitante abre. Cada uma quer o seu subdomínio.

Antes de começar

  • VPS com Docker instalado — veja o guia de Docker.
  • Dois subdomínios apontados para o IP: bot.seudominio.com.br (builder) e chat.seudominio.com.br (viewer).
  • 4 GB de RAM é o que recomendo. São duas aplicações Node, mais PostgreSQL e MinIO; em 2 GB fica apertado e o builder engasga.
  • Um servidor SMTP para os e-mails de login (o Typebot autentica por link mágico).

Aponte os dois subdomínios

Crie dois registros A, ambos para o IP da VPS. Sem os dois, o certificado de um deles falha e metade do sistema não abre.

terminal
dig +short bot.seudominio.com.br
dig +short chat.seudominio.com.br

Gere as chaves e o arquivo de ambiente

O Typebot exige uma chave de criptografia de exatamente 32 caracteres. Menos do que isso e a aplicação nem sobe.

terminal
mkdir -p /opt/typebot && cd /opt/typebot

cat > .env <<EOF
DOMINIO_BUILDER=bot.seudominio.com.br
DOMINIO_VIEWER=chat.seudominio.com.br
[email protected]

ENCRYPTION_SECRET=$(openssl rand -base64 24 | cut -c1-32)
POSTGRES_PASSWORD=$(openssl rand -hex 24)
MINIO_PASSWORD=$(openssl rand -hex 20)

# SMTP para o link de login
SMTP_HOST=smtp.seudominio.com.br
SMTP_PORT=587
[email protected]
SMTP_PASSWORD=troque-aqui
[email protected]
EOF

chmod 600 .env
grep ENCRYPTION_SECRET .env | cut -d= -f2 | wc -c

O último comando precisa responder 33 — são 32 caracteres mais a quebra de linha.

Escreva o docker-compose.yml

São cinco serviços: PostgreSQL, MinIO (armazena os arquivos que os visitantes enviam), builder, viewer e o Caddy na frente dos dois domínios.

terminal
cat > /opt/typebot/docker-compose.yml <<'EOF'
services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: typebot
      POSTGRES_USER: typebot
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U typebot"]
      interval: 10s
      retries: 5

  minio:
    image: minio/minio
    restart: unless-stopped
    command: server /data
    environment:
      MINIO_ROOT_USER: typebot
      MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD}
    volumes:
      - minio_data:/data

  criar-bucket:
    image: minio/mc
    depends_on:
      - minio
    entrypoint: >
      /bin/sh -c "
      until (/usr/bin/mc alias set minio http://minio:9000 typebot \"$$MINIO_PASSWORD\"); do sleep 1; done;
      /usr/bin/mc mb --ignore-existing minio/typebot;
      /usr/bin/mc anonymous set download minio/typebot/public;
      exit 0;
      "
    environment:
      MINIO_PASSWORD: ${MINIO_PASSWORD}

  builder:
    image: baptistearno/typebot-builder:latest
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DATABASE_URL: postgresql://typebot:${POSTGRES_PASSWORD}@postgres:5432/typebot
      ENCRYPTION_SECRET: ${ENCRYPTION_SECRET}
      NEXTAUTH_URL: https://${DOMINIO_BUILDER}
      NEXT_PUBLIC_VIEWER_URL: https://${DOMINIO_VIEWER}
      ADMIN_EMAIL: ${EMAIL}
      SMTP_HOST: ${SMTP_HOST}
      SMTP_PORT: ${SMTP_PORT}
      SMTP_USERNAME: ${SMTP_USERNAME}
      SMTP_PASSWORD: ${SMTP_PASSWORD}
      NEXT_PUBLIC_SMTP_FROM: 'Typebot <${EMAIL_REMETENTE}>'
      S3_ACCESS_KEY: typebot
      S3_SECRET_KEY: ${MINIO_PASSWORD}
      S3_BUCKET: typebot
      S3_ENDPOINT: minio
      S3_PORT: 9000
      S3_SSL: "false"

  viewer:
    image: baptistearno/typebot-viewer:latest
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DATABASE_URL: postgresql://typebot:${POSTGRES_PASSWORD}@postgres:5432/typebot
      ENCRYPTION_SECRET: ${ENCRYPTION_SECRET}
      NEXTAUTH_URL: https://${DOMINIO_BUILDER}
      NEXT_PUBLIC_VIEWER_URL: https://${DOMINIO_VIEWER}
      S3_ACCESS_KEY: typebot
      S3_SECRET_KEY: ${MINIO_PASSWORD}
      S3_BUCKET: typebot
      S3_ENDPOINT: minio
      S3_PORT: 9000
      S3_SSL: "false"

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    environment:
      DOMINIO_BUILDER: ${DOMINIO_BUILDER}
      DOMINIO_VIEWER: ${DOMINIO_VIEWER}
      EMAIL: ${EMAIL}
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

volumes:
  postgres_data:
  minio_data:
  caddy_data:
  caddy_config:
EOF

cat > /opt/typebot/Caddyfile <<'EOF'
{$DOMINIO_BUILDER} {
	tls {$EMAIL}
	reverse_proxy builder:3000
}

{$DOMINIO_VIEWER} {
	tls {$EMAIL}
	reverse_proxy viewer:3000
}
EOF

Suba e crie a conta

terminal
cd /opt/typebot
docker compose up -d
docker compose ps

# o builder demora mais na primeira subida: roda as migrações do banco
docker compose logs -f builder

Abra https://bot.seudominio.com.br. O login é por link enviado no e-mail — por isso o SMTP precisa estar certo. O endereço que estiver em ADMIN_EMAIL vira administrador da instalação.

Se o e-mail não chegar, você ainda consegue o link de acesso no log: docker compose logs builder | grep -i "sign in".

Publique o primeiro fluxo

No builder, crie um typebot, monte as perguntas e clique em Publish. O endereço público sai no domínio do viewer, e é ele que você incorpora no site ou envia por link.

Para embutir numa página, o Typebot gera o trecho de código pronto em Share → Embed.

Configure o backup

O que importa é o banco (fluxos e respostas) e o MinIO (arquivos enviados).

terminal
cat > /opt/typebot/backup.sh <<'EOF'
#!/bin/bash
set -e
cd /opt/typebot
source .env
DESTINO=/opt/typebot/backups
mkdir -p $DESTINO
DATA=$(date +%Y%m%d-%H%M)

docker compose exec -T postgres pg_dump -U typebot typebot | gzip > "$DESTINO/typebot-$DATA.sql.gz"
docker run --rm -v typebot_minio_data:/dados -v "$DESTINO":/backup alpine tar czf "/backup/arquivos-$DATA.tar.gz" -C /dados .

find "$DESTINO" -name '*.gz' -mtime +7 -delete
EOF

chmod +x /opt/typebot/backup.sh
(crontab -l 2>/dev/null; echo '0 3 * * * /opt/typebot/backup.sh') | crontab -

Se algo não funcionar

O builder não sobe e o log fala da chave de criptografia

O ENCRYPTION_SECRET precisa ter exatamente 32 caracteres. Confira com grep ENCRYPTION_SECRET .env | cut -d= -f2 | wc -c — a resposta deve ser 33, contando a quebra de linha.

O e-mail de login nunca chega

Pegue o link direto do log enquanto ajusta o SMTP:

terminal
docker compose logs builder | grep -i 'sign in' | tail -3

O fluxo publicado abre em branco

Quase sempre é o NEXT_PUBLIC_VIEWER_URL apontando para o domínio errado. Corrija no .env e recrie os dois contêineres — variáveis NEXT_PUBLIC_ são gravadas na subida e não mudam sozinhas.

terminal
docker compose up -d --force-recreate builder viewer
VPS de 4 GB para rodar builder e viewer com folga

Memória reservada no KVM, NVMe Gen 4 e rota nacional. Ativação automática.

Ver planos de VPS

Perguntas frequentes

Preciso mesmo de dois subdomínios?

Sim. O builder e o viewer são aplicações separadas e o Typebot usa a distinção entre elas para montar os links públicos dos fluxos. Tentar servir as duas no mesmo domínio gera conflito de rotas e sessão.

Roda em 2 GB de RAM?

Sobe, mas fica no limite: são duas aplicações Next.js, PostgreSQL e MinIO no mesmo host. O builder é o que mais consome enquanto você edita. Para uso real, 4 GB.

Dá para conectar o Typebot ao WhatsApp?

Dá. O caminho comum é o fluxo chamar um webhook do n8n, que conversa com a Evolution API. As três aplicações convivem na mesma VPS, se houver memória.

Continue por aqui