🔓 Como instalar a Evolution API (latest) no Portainer – Docker Swarm

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_public e resolver de SSL le configurado).
  • PostgreSQL (acessível na rede digital_network pelo hostname postgres).
  • Redis (acessível na rede digital_network pelo hostname redis).
  • 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:

Contrate sua VPS na Hostinger para rodar sua stack self-hosted

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;"
Pule este passo se o banco do Chatwoot já existir — a integração apenas se conecta a ele para leitura.

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
Por que esse passo existe: impede que o Swarm crie um volume efêmero atrelado a um nó específico, garantindo que as instâncias conectadas não caiam se o container reiniciar.

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_KEY definida no Passo 5 para criar sua primeira instância e escanear o QR Code.

Resolução de Problemas Comuns na Evolution API v2

SintomaCausaSolução
QR Code não gera / expira rápidoRedis inacessível ou CACHE_REDIS_URI incorretoConfirme se o serviço redis está na rede digital_network e se a URI aponta para o hostname certo
Erro ao salvar mídiasMinIO fora do ar ou credenciais S3 erradasRevise S3_ACCESS_KEY, S3_SECRET_KEY e S3_ENDPOINT nas variáveis de ambiente
Mensagens não chegam ao ChatwootCHATWOOT_IMPORT_DATABASE_CONNECTION_URI incorreta ou CHATWOOT_ENABLED=falseConfirme a senha do Postgres e se o banco do Chatwoot está acessível pela mesma rede
Webhook não dispara para o n8nWEBHOOK_GLOBAL_URL vazioPreencha 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

Menu Interativo

~/digitalinterativo/trilhas.sh — aulas
/
#!/usr/bin/trilha --interativa --self-hosted
🔓 aberto🔐 exclusivo aluno⏳ em breve
COMPARTILHAR