Nesta aula vamos instalar a Evolution API v2 do zero na sua VPS, usando Portainer, Docker Swarm e Traefik, já integrada ao Chatwoot, Redis e MinIO. Diferente de tutoriais genéricos, aqui cada passo existe para evitar que a API perca conexão com as instâncias do WhatsApp ou falhe ao salvar mídias. Siga estritamente na ordem.
Pré-requisitos
Antes de começar, seu ambiente já precisa ter rodando:
- Portainer (gerenciador do Docker Swarm).
- Traefik (proxy reverso, com rede
traefik_publice resolver de SSLleconfigurado). - PostgreSQL (acessível na rede
digital_networkpelo hostnamepostgres). - Redis (acessível na rede
digital_networkpelo hostnameredis). - MinIO (para armazenamento de mídias via S3).
- Chatwoot (opcional, mas necessário se for usar a integração nativa).
- DNS configurado: aponte o subdomínio da API (ex:
evolution.seudominio.com.br) e o do MinIO Console (ex:miniobrowser.seu_subdominio_api.com.br) para o IP do seu servidor.
Link das aulas pré-requisitas abaixo:
Passo a Passo
Passo 1: Autenticar no Docker Hub (evita erro toomanyrequests)
Faça login no Docker Hub direto no terminal do servidor:
docker login
Passo 2: Baixar a imagem manualmente antes do deploy
Baixe a imagem da versão 2.4.0-rc2:
docker pull evoapicloud/evolution-api:2.4.0-rc2
Passo 3: Criar os bancos de dados no PostgreSQL
A Evolution API v2 precisa do seu próprio banco, e a integração com o Chatwoot lê o banco dele. Acesse o container do seu Postgres:
PG=$(docker ps -qf name=postgres) docker exec -i $PG psql -U postgres -c "CREATE DATABASE evolution2;"
Passo 4: Criar o volume externo
A Evolution guarda as sessões/instâncias do WhatsApp em volume próprio:
docker volume create evolution_api_v2_data
Passo 5: Preparar as variáveis de ambiente
Gere uma chave forte para a API e separe as credenciais do MinIO (Access/Secret Key):
openssl rand -hex 32
Monte o bloco de Environment Variables com domínio, senha do Postgres, nomes dos bancos, credenciais S3 e a API key:
# ========================================== # DOMÍNIO # ========================================== EVOLUTION_DOMAIN=evolution.seudominio.com.br # ========================================== # BANCO DE DADOS (POSTGRESQL) # ========================================== POSTGRES_PASSWORD=sua_senha_do_banco_aqui POSTGRES_DATABASE_EVOLUTION=evolution2 POSTGRES_DATABASE_CHATWOOT=chatwoot # ========================================== # MINIO / S3 # ========================================== S3_ACCESS_KEY=sua_access_key_aqui S3_SECRET_KEY=sua_secret_key_aqui S3_BUCKET=evolution2 S3_ENDPOINT=miniobrowser.seu_subdominio_api.com.br # ========================================== # SEGURANÇA # ========================================== AUTHENTICATION_API_KEY=gere_uma_chave_segura_aqui
Passo 6: Ajustar e colar a stack no Portainer
Vá em Stacks → Add stack, dê o nome de evolution-api-v2 e cole o código docker-compose.yml da stack. Abaixo do editor, ative Environment variables, clique em Advanced mode e cole o bloco do Passo 5. Clique em Deploy the stack.
⬇️ Ver e copiar a stack completa
Passo 7: Acompanhar o boot
Acompanhe os logs do serviço para confirmar que ele subiu sem erro de conexão com Postgres/Redis:
docker service logs evolution-api-v2_evolution_api_v2_data --tail 50 -f
Passo 8: Primeiro acesso
Quando o serviço estiver com status Running (em verde) no Portainer:
- Acesse o domínio configurado:
https://evolution.seudominio.com.br. - Use o Manager (ou a API diretamente) com a
AUTHENTICATION_API_KEYdefinida no Passo 5 para criar sua primeira instância e escanear o QR Code.
Resolução de Problemas Comuns na Evolution API v2
| Sintoma | Causa | Solução |
|---|---|---|
| QR Code não gera / expira rápido | Redis inacessível ou CACHE_REDIS_URI incorreto | Confirme se o serviço redis está na rede digital_network e se a URI aponta para o hostname certo |
| Erro ao salvar mídias | MinIO fora do ar ou credenciais S3 erradas | Revise S3_ACCESS_KEY, S3_SECRET_KEY e S3_ENDPOINT nas variáveis de ambiente |
| Mensagens não chegam ao Chatwoot | CHATWOOT_IMPORT_DATABASE_CONNECTION_URI incorreta ou CHATWOOT_ENABLED=false | Confirme a senha do Postgres e se o banco do Chatwoot está acessível pela mesma rede |
| Webhook não dispara para o n8n | WEBHOOK_GLOBAL_URL vazio | Preencha com a URL do webhook do n8n antes do deploy |
Stack Completa – Evolution API v2
Copie o código abaixo e cole direto no editor da sua stack no Portainer.
version: "3.8"
services:
evolution_api_v2_data:
image: evoapicloud/evolution-api:2.4.0-rc2
volumes:
- evolution_api_v2_data:/evolution/instances
networks:
- traefik_public
- digital_network
environment:
- SERVER_URL=https://${EVOLUTION_DOMAIN}
- DEL_INSTANCE=false
- DATABASE_PROVIDER=postgresql
- DATABASE_CONNECTION_URI=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DATABASE_EVOLUTION}
- DATABASE_SAVE_DATA_INSTANCE=true
- DATABASE_SAVE_DATA_NEW_MESSAGE=true
- DATABASE_SAVE_MESSAGE_UPDATE=true
- DATABASE_SAVE_DATA_CONTACTS=true
- DATABASE_SAVE_DATA_CHATS=true
- DATABASE_SAVE_DATA_LABELS=true
- DATABASE_SAVE_DATA_HISTORIC=true
- DATABASE_CONNECTION_CLIENT_NAME=evolution_api_v2
## Sobre o QR-Code
- QRCODE_LIMIT=1902
- QRCODE_COLOR=#000000
#Webhook
- WEBHOOK_GLOBAL_URL=''
- WEBHOOK_GLOBAL_ENABLED=true
- WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=true
- WEBHOOK_EVENTS_APPLICATION_STARTUP=true
- WEBHOOK_EVENTS_QRCODE_UPDATED=true
- WEBHOOK_EVENTS_MESSAGES_SET=true
- WEBHOOK_EVENTS_MESSAGES_UPSERT=true
- WEBHOOK_EVENTS_MESSAGES_EDITED=true
- WEBHOOK_EVENTS_MESSAGES_UPDATE=true
- WEBHOOK_EVENTS_MESSAGES_DELETE=true
- WEBHOOK_EVENTS_SEND_MESSAGE=true
- WEBHOOK_EVENTS_CONTACTS_SET=true
- WEBHOOK_EVENTS_CONTACTS_UPSERT=true
- WEBHOOK_EVENTS_CONTACTS_UPDATE=true
- WEBHOOK_EVENTS_PRESENCE_UPDATE=true
- WEBHOOK_EVENTS_CHATS_SET=true
- WEBHOOK_EVENTS_CHATS_UPSERT=true
- WEBHOOK_EVENTS_CHATS_UPDATE=true
- WEBHOOK_EVENTS_CHATS_DELETE=true
- WEBHOOK_EVENTS_GROUPS_UPSERT=true
- WEBHOOK_EVENTS_GROUPS_UPDATE=true
- WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
- WEBHOOK_EVENTS_CONNECTION_UPDATE=true
- WEBHOOK_EVENTS_LABELS_EDIT=true
- WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
- WEBHOOK_EVENTS_CALL=true
- WEBHOOK_EVENTS_TYPEBOT_START=true
- WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS=true
- WEBHOOK_EVENTS_ERRORS=false
- WEBHOOK_EVENTS_ERRORS_WEBHOOK=false
- CONFIG_SESSION_PHONE_CLIENT=EvolutionAPIV2
- CONFIG_SESSION_PHONE_NAME=Chrome
- QRCODE_LIMIT=999
#Chatwoot
- CHATWOOT_ENABLED=true
- CHATWOOT_MESSAGE_READ=true
- CHATWOOT_MESSAGE_DELETE=true
- CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DATABASE_CHATWOOT}?sslmode=disable
- CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE=true
#redis
- CACHE_REDIS_ENABLED=true
- CACHE_REDIS_URI=redis://redis:6379/6
- CACHE_REDIS_PREFIX_KEY=evolution_api_v2
- CACHE_REDIS_SAVE_INSTANCES=false
- CACHE_LOCAL_ENABLED=false
#Minio
- S3_ENABLED=true
- S3_ACCESS_KEY=${S3_ACCESS_KEY}
- S3_SECRET_KEY=${S3_SECRET_KEY}
- S3_BUCKET=${S3_BUCKET}
- S3_PORT=443
- S3_ENDPOINT=${S3_ENDPOINT}
- S3_USE_SSL=true
#API KEY
- AUTHENTICATION_API_KEY=${AUTHENTICATION_API_KEY}
- AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true
- LANGUAGE=en
# Proxy
#- GLOBAL_PROXY_ENABLED=true
#- GLOBAL_PROXY_PROTOCOL=http
#- GLOBAL_PROXY_HOST=3proxy
#- GLOBAL_PROXY_PORT=8000
#- GLOBAL_PROXY_USERNAME=evolution
#- GLOBAL_PROXY_PASSWORD=MINHA_SENHA_AQUI
- N8N_ENABLED=true
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.role == manager
labels:
- traefik.enable=true
- traefik.http.routers.evolution_api_v2.rule=Host(`${EVOLUTION_DOMAIN}`)
- traefik.http.routers.evolution_api_v2.entrypoints=websecure
- traefik.http.routers.evolution_api_v2.tls.certresolver=le
- traefik.http.routers.evolution_api_v2.priority=1
- traefik.http.routers.evolution_api_v2.service=evolution_api_v2
- traefik.http.services.evolution_api_v2.loadbalancer.server.port=8080
- traefik.http.services.evolution_api_v2.loadbalancer.passHostHeader=true
volumes:
evolution_api_v2_data:
external: true
name: evolution_api_v2_data
networks:
traefik_public:
external: true
digital_network:
external: truel
