Pular para o conteúdo
Docker e VPS

Chatwoot: instalando na VPS o atendimento omnichannel

Um unicórnio de crina colorida e uma coruja com varinha num balcão mágico de atendimento, sob um arco de castelo com velas flutuantes, enquanto balões de conversa chegam de um envelope, de um chat e de um celular e caem numa única caixa de entrada brilhante

Olá meus Unicórnios! 🦄✨

Sabe quando o cliente manda mensagem no chat do site, depois pergunta a mesma coisa no WhatsApp, e ainda escreve um e-mail "só para garantir"? 😅 Três lugares, três abas abertas, e ninguém sabe quem já respondeu o quê. Eu queria um lugar só para tudo isso, e sem pagar por atendente.

O Chatwoot é exatamente esse lugar. É uma central de atendimento de código aberto: você instala no seu servidor, conecta os canais (chat do site, e-mail, WhatsApp, Telegram e outros) e a sua equipe responde tudo numa caixa de entrada única.

Parecia uma instalação de cinco minutos, porque a documentação oficial cabe em quatro comandos. Só que o terceiro comando ficou parado para sempre, repetindo a mesma linha, sem erro nenhum. 🤯 Spoiler: a senha que eu tinha escrito com todo o cuidado estava no arquivo certo, mas faltava num segundo arquivo que ninguém avisa.

Neste artigo eu mostro o caminho inteiro, numa VPS Ubuntu: o que é o Chatwoot, como instalar com Docker (explicando cada comando, mesmo que você nunca tenha mexido num servidor), como colocar um endereço com HTTPS na frente, como configurar a primeira caixa de entrada e como atender o primeiro cliente pelo chat do site.

Chatwoot: instalação com DockerA documentação oficial, com os dois arquivos que vamos baixar e os comandos de referência.developers.chatwoot.com

💬 O que é o Chatwoot

Pense numa caixa de entrada de e-mail, só que para todas as conversas com clientes. Cada canal que você conecta vira uma "caixa de entrada" dentro do Chatwoot, e todas desembocam no mesmo painel. É isso que "omnichannel" quer dizer: vários canais, um atendimento só.

Na versão que eu instalei, a 4.18.0, estes são alguns dos canais que aparecem para conectar:

  • Site: um balãozinho de chat que você cola no seu site. É o que vamos montar neste artigo, porque não depende de conta em nenhum outro serviço.
  • E-mail: Gmail, Outlook ou qualquer provedor.
  • WhatsApp, Telegram, Line e SMS.
  • Facebook e Instagram, que aparecem apagados até você configurar um aplicativo da Meta.
  • API: um canal genérico, para você ligar qualquer outro sistema.

E, por cima dos canais, as ferramentas de uma equipe de atendimento: vários atendentes (o Chatwoot chama de agentes), distribuição automática das conversas, etiquetas, respostas prontas, mensagens privadas entre a equipe e relatórios. Tudo no seu servidor, com os dados dos seus clientes na sua máquina.

🖥️ O que a VPS precisa ter

Usei uma VPS com Ubuntu 24.04, 4 vCPU e 7,8 GB de RAM, recém-formatada. O Chatwoot roda em quatro "caixinhas" (contêineres) e, com tudo de pé, eu medi estes números de memória:

NAME                  MEM USAGE / LIMIT
chatwoot-sidekiq-1    573.2MiB / 7.755GiB
chatwoot-rails-1      450.7MiB / 7.755GiB
chatwoot-redis-1      4.395MiB / 7.755GiB
chatwoot-postgres-1   125.9MiB / 7.755GiB

Somando, perto de 1,2 GB. A imagem principal do Chatwoot ocupa 2,71 GB de disco. Uma VPS de 2 GB fica no limite; com 4 GB você trabalha tranquila.

Além da VPS, você vai precisar de um domínio (ou subdomínio) apontando para ela. Neste artigo eu uso chat.seudominio.com.br: no painel onde você comprou o domínio, crie um registro do tipo A com o nome chat e o IP da sua VPS. Faça isso agora, porque a mudança pode levar alguns minutos para valer, e lá no fim a gente vai precisar dela.

Para entrar na VPS, abra o terminal do seu computador (no Windows, o PowerShell; no Mac e no Linux, o Terminal) e conecte com o ssh, trocando pelo IP da sua máquina:

ssh root@SEU_IP_AQUI

Ele pede a senha. Um aviso que trava todo mundo na primeira vez: enquanto você digita a senha, nada aparece na tela, nem asterisco. Não está travado, é assim mesmo. Digite e aperte Enter.

O Chatwoot roda em Docker, que é um jeito de rodar um programa dentro de uma "caixinha" com tudo de que ele precisa, sem instalar Ruby, banco de dados nem biblioteca nenhuma direto na sua máquina. A minha VPS era nova e não tinha Docker. O próprio Docker mantém um script de instalação, e é um comando só:

curl -fsSL https://get.docker.com | sh

Ele demora um ou dois minutos e escreve bastante coisa na tela. Quando terminar, confira que o Docker e o docker compose (quem lê o arquivo de configuração do Chatwoot) estão lá:

docker --version
docker compose version
Docker version 29.8.1, build 4a63305
Docker Compose version v5.5.1

📥 Baixando os dois arquivos oficiais

O Chatwoot inteiro é descrito por dois arquivos: o .env, com as configurações e senhas, e o docker-compose.yaml, que diz quais caixinhas subir. Primeiro, uma pasta só para eles. O mkdir -p cria a pasta, e o cd entra nela:

mkdir -p /opt/chatwoot
cd /opt/chatwoot

Agora o wget baixa os dois arquivos direto do repositório oficial. O -O dá o nome com que cada um vai ser salvo:

wget -O .env https://raw.githubusercontent.com/chatwoot/chatwoot/develop/.env.example
wget -O docker-compose.yaml https://raw.githubusercontent.com/chatwoot/chatwoot/develop/docker-compose.production.yaml

Para ver os arquivos, use ls -la. O -la importa: arquivo cujo nome começa com ponto, como o .env, fica escondido no ls comum. Não é que ele não baixou, é que o Linux esconde esses arquivos por padrão.

ls -la
total 28
drwxr-xr-x 2 root root  4096 Sep 25 16:30 .
drwxr-xr-x 4 root root  4096 Sep 25 16:30 ..
-rw-r--r-- 1 root root 14021 Sep 25 16:30 .env
-rw-r--r-- 1 root root  1417 Sep 25 16:30 docker-compose.yaml

📝 Preenchendo o .env

O .env tem 351 linhas, mas calma: para começar, você mexe em cinco. A primeira é uma chave secreta que o Chatwoot usa para assinar os logins. Ela precisa ser longa e aleatória, e o openssl gera uma para você:

openssl rand -hex 64

Ele imprime uma sequência de 128 letras e números. Selecione e copie (no terminal, selecionar com o mouse já costuma copiar; se não, Ctrl+Shift+C). Rode o mesmo comando mais duas vezes, trocando o 64 por 16, para ter as senhas do banco de dados (Postgres) e do Redis. Guarde as três num bloco de notas.

Agora abra o arquivo no nano, o editor de texto mais simples do terminal:

nano .env

O nano abre o arquivo e mostra no rodapé os atalhos, como ^W Where Is. O ^ quer dizer a tecla Ctrl. Para achar uma linha num arquivo tão grande, aperte Ctrl+W, digite o começo dela (por exemplo SECRET_KEY_BASE) e aperte Enter: o cursor pula direto para lá. Ande com as setas do teclado, apague o valor antigo e digite (ou cole com Ctrl+Shift+V) o novo.

Estas são as cinco linhas, do jeito que elas devem ficar, com os seus valores no lugar:

SECRET_KEY_BASE=cole_aqui_a_chave_de_128_caracteres
FRONTEND_URL=https://chat.seudominio.com.br
DEFAULT_LOCALE=pt_BR
REDIS_PASSWORD=cole_aqui_a_senha_do_redis
POSTGRES_PASSWORD=cole_aqui_a_senha_do_postgres

O que cada uma faz:

  • SECRET_KEY_BASE: a chave secreta. Não use símbolos, só letras e números (o próprio arquivo pede isso num comentário), e é por isso que usamos o -hex.
  • FRONTEND_URL: o endereço pelo qual o Chatwoot vai ser acessado, com https://. Ele vai parar dentro do código do chat do site, então precisa ser o endereço final.
  • DEFAULT_LOCALE: essa vem comentada, como # DEFAULT_LOCALE=en. Apague o # do começo e troque en por pt_BR. É o que deixa a tela de login e o chat do site em português.
  • REDIS_PASSWORD e POSTGRES_PASSWORD: as senhas do Redis (uma memória rápida onde o Chatwoot guarda as filas de trabalho) e do Postgres (o banco de dados). Vêm vazias.

Duas linhas que você não precisa mexer, mas vale saber que existem: ENABLE_ACCOUNT_SIGNUP=false, que impede estranhos de criarem conta no seu Chatwoot, e RAILS_ENV=development, que parece errada mas é ignorada, porque o docker-compose.yaml força production nos serviços do Chatwoot.

Para salvar no nano: Ctrl+O (de "output"), Enter para confirmar o nome, e Ctrl+X para sair.

😱 A senha que precisa estar em dois lugares

Se você só for ler um pedaço deste artigo, leia este. 🙏

Com o .env pronto, a documentação manda preparar o banco de dados. Eu rodei, e ele começou a baixar as imagens, subiu o Postgres e o Redis, e ficou nisto aqui:

docker compose run --rm rails bundle exec rails db:chatwoot_prepare
Waiting for postgres to become ready....
+ echo 'Waiting for postgres to become ready....'
+ docker/entrypoints/helpers/pg_database_url.rb
+ export 'POSTGRES_PORT=5432'
+ PG_READY='pg_isready -h postgres -p 5432 -U postgres'
+ pg_isready -h postgres -p 5432 -U postgres
postgres:5432 - no response
+ sleep 2
+ pg_isready -h postgres -p 5432 -U postgres
postgres:5432 - no response
+ sleep 2

E a linha postgres:5432 - no response se repetindo de dois em dois segundos. Esperei vários minutos, e o registro juntou 79 repetições dessa linha: nenhum erro, nenhuma pista. Ele não desiste, e não diz o que está errado.

A pista estava no registro do próprio Postgres, que eu só fui olhar depois. Em outra janela do terminal (ou depois de parar o comando com Ctrl+C), dentro da pasta /opt/chatwoot, peça o registro dele. Recortei a parte que interessa:

docker compose logs postgres
postgres-1  | Error: Database is uninitialized and superuser password is not specified.
postgres-1  |        You must specify POSTGRES_PASSWORD to a non-empty value for the
postgres-1  |        superuser. For example, "-e POSTGRES_PASSWORD=password" on "docker run".
postgres-1  |
postgres-1  |        You may also use "POSTGRES_HOST_AUTH_METHOD=trust" to allow all
postgres-1  |        connections without a password. This is *not* recommended.

"A senha do superusuário não foi informada." Mas eu tinha informado! Estava lá no .env, bonitinha. 😳

O detalhe cruel: o .env é lido pelos serviços do Chatwoot (o rails e o sidekiq), que usam a senha para se conectar ao banco. Mas o serviço do Postgres, que precisa da senha para criar o banco, não lê o .env. Ele lê um bloco próprio dentro do docker-compose.yaml, e lá a linha vem vazia:

  postgres:
    image: pgvector/pgvector:pg16
    restart: always
    ports:
      - '127.0.0.1:5432:5432'
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=chatwoot
      - POSTGRES_USER=postgres
      # Please provide your own password.
      - POSTGRES_PASSWORD=

O Postgres se recusa a iniciar sem senha, o Docker tenta de novo (é o restart: always), ele se recusa de novo, e o preparo do banco fica esperando alguém que nunca vai atender. A solução é colocar ali a mesma senha que está no POSTGRES_PASSWORD do .env. Tem de ser idêntica: se as duas forem diferentes, o Postgres sobe, mas o Chatwoot não consegue entrar nele.

Antes de consertar, desfaça a tentativa que falhou. O down derruba as caixinhas, e o volume rm apaga os "discos" que elas criaram, para o Postgres começar do zero:

docker compose down
docker volume rm chatwoot_postgres_data chatwoot_redis_data chatwoot_storage_data

Agora abra o docker-compose.yaml:

nano docker-compose.yaml

Ache a linha - POSTGRES_PASSWORD= (com Ctrl+W, como antes) e cole a senha logo depois do =, sem espaço. Já que você está no arquivo, apague também a primeira linha, version: '3': ela é de um formato antigo, e o Docker reclama dela a cada comando com este aviso:

level=warning msg="/opt/chatwoot/docker-compose.yaml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion"

Salve (Ctrl+O, Enter, Ctrl+X) e prepare o banco de novo.

🗄️ Preparando o banco de dados

docker compose run --rm rails bundle exec rails db:chatwoot_prepare

Desta vez o Postgres responde em segundos. E aí vem um susto no meio da saída, que eu recortei aqui:

Waiting for postgres to become ready....
postgres:5432 - accepting connections
Database ready to accept connections.
Bundle complete! 152 Gemfile dependencies, 302 gems now installed.
E, [2026-09-25T14:42:11.624544 #1] ERROR -- : Failed to configure AI Agents SDK: We could not find your database: chatwoot_production. Available database configurations can be found in config/database.yml.
I, [2026-09-25T14:42:11.625892 #1]  INFO -- : [rake ip_lookup:setup] IP_LOOKUP_API_KEY empty. Skipping geoip database setup
Created database 'chatwoot_production'

Um ERROR dizendo que o banco chatwoot_production não existe. E ele não existe mesmo: é justamente o que este comando veio criar, e a última linha mostra Created database 'chatwoot_production'. Um pedaço do Chatwoot tenta ler o banco antes de ele nascer e reclama. Pode ignorar. O que importa é o comando terminar e devolver o terminal para você.

🚀 Subindo o Chatwoot

O up -d sobe tudo, e o -d deixa rodando em segundo plano, para você poder fechar o terminal:

docker compose up -d

Espere uns trinta segundos e confira as caixinhas com o docker compose ps:

docker compose ps
NAME                  IMAGE                      COMMAND                  SERVICE    CREATED          STATUS          PORTS
chatwoot-postgres-1   pgvector/pgvector:pg16     "docker-entrypoint.s…"   postgres   29 minutes ago   Up 28 minutes   127.0.0.1:5432->5432/tcp
chatwoot-rails-1      chatwoot/chatwoot:latest   "docker/entrypoints/…"   rails      28 minutes ago   Up 28 minutes   127.0.0.1:3000->3000/tcp
chatwoot-redis-1      redis:alpine               "docker-entrypoint.s…"   redis      29 minutes ago   Up 28 minutes   127.0.0.1:6379->6379/tcp
chatwoot-sidekiq-1    chatwoot/chatwoot:latest   "bundle exec sidekiq…"   sidekiq    28 minutes ago   Up 28 minutes   3000/tcp

Quatro serviços, todos Up: o rails é o Chatwoot que você acessa, o sidekiq faz o trabalho de bastidor (enviar mensagem, distribuir conversa), e o postgres e o redis guardam os dados.

Se você rodar docker compose ps -a (com o -a, de "all"), aparece um quinto, o chatwoot-base-1, com Exited (0). Não é defeito: ele é só o "molde" que o arquivo usa para montar o rails e o sidekiq, e termina logo depois de subir. O (0) quer dizer que saiu sem erro.

Para ter certeza de que o Chatwoot responde, pergunte a ele pela API:

curl -s localhost:3000/api
{
    "version": "4.18.0",
    "timestamp": "2026-09-25 14:43:07",
    "queue_services": "ok",
    "data_services": "ok"
}

queue_services é o Redis, data_services é o Postgres. Os dois ok. 🎉

🌐 Nginx e HTTPS: o endereço de verdade

Repare na coluna PORTS ali em cima: todas as portas estão em 127.0.0.1, o endereço que quer dizer "a própria máquina". Isso é de propósito, no arquivo oficial. Se você abrir http://SEU_IP_AQUI:3000 no navegador, não abre nada. E o banco e o Redis também ficam invisíveis para a internet, o que é ótimo.

Só que o Chatwoot precisa ser alcançado de fora: você usa o painel pelo navegador, e o chat do site roda no navegador dos seus clientes, que vão buscar o Chatwoot pelo endereço do FRONTEND_URL. Quem faz essa ponte é o Nginx, um servidor web que recebe as visitas na porta 443 (HTTPS) e repassa para o 127.0.0.1:3000. E o certificado HTTPS vem de graça do Let's Encrypt, pelo Certbot. Instale os dois:

apt-get update
apt-get install -y nginx certbot python3-certbot-nginx

Agora crie o arquivo de configuração do Chatwoot no Nginx:

nano /etc/nginx/sites-available/chatwoot

Cole o bloco inteiro abaixo, trocando chat.seudominio.com.br pelo seu endereço:

server {
    listen 80;
    server_name chat.seudominio.com.br;

    # O Chatwoot aceita anexos; o padrão do Nginx (1 MB) barraria os maiores
    client_max_body_size 40M;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Sem estas tres linhas, as mensagens novas nao aparecem sozinhas na tela
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

O proxy_pass é o repasse para o Chatwoot. As três últimas linhas são as que parecem enfeite e não são: mais para o fim do artigo eu mostro o que acontece quando elas faltam. Salve e saia (Ctrl+O, Enter, Ctrl+X).

O Nginx só lê os arquivos que estão na pasta sites-enabled. O ln -s cria um "atalho" do arquivo para lá, o nginx -t confere se não tem erro de digitação, e o reload aplica:

ln -s /etc/nginx/sites-available/chatwoot /etc/nginx/sites-enabled/chatwoot
nginx -t
systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Agora o certificado. O Certbot confere que o domínio aponta para esta VPS, pede o certificado ao Let's Encrypt e ainda edita o arquivo do Nginx sozinho para ligar o HTTPS. O --agree-tos aceita os termos de uso, o --register-unsafely-without-email dispensa cadastrar um e-mail (o nome assusta, mas a renovação é automática), e o -n faz tudo sem perguntas:

certbot --nginx -d chat.seudominio.com.br --agree-tos --register-unsafely-without-email -n
Account registered.
Requesting a certificate for chat.seudominio.com.br

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/chat.seudominio.com.br/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/chat.seudominio.com.br/privkey.pem
This certificate expires on 2026-12-24.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

Deploying certificate
Successfully deployed certificate for chat.seudominio.com.br to /etc/nginx/sites-enabled/chatwoot
Congratulations! You have successfully enabled HTTPS on https://chat.seudominio.com.br

Se aparecer um erro de DNS ou unauthorized aqui, quase sempre é o registro A do domínio que ainda não passou a valer. Espere uns minutos e rode de novo.

🔑 A primeira tela: crie o administrador já

Abra https://chat.seudominio.com.br no navegador. O Chatwoot mostra a tela de instalação, ainda em inglês:

A tela de instalação do Chatwoot, Howdy, Welcome to Chatwoot, com os campos Name, Company Name, Work Email e Password preenchidos com Ana Exemplo, Loja Exemplo e admin@exemplo.com, e a caixa de newsletter desmarcada

Aqui mora um cuidado importante: essa tela não pede login. Quem preencher primeiro vira o administrador geral do seu Chatwoot. Então não deixe para depois: assim que o HTTPS funcionar, preencha o seu nome, o nome da empresa, o seu e-mail e uma senha forte. Eu desmarquei a caixinha da newsletter e cliquei em Finish Setup.

Depois disso, o endereço dessa tela passa a redirecionar para a página inicial, e ninguém mais cria administrador por ela.

Entrando com o e-mail e a senha, o Chatwoot pede os detalhes da conta, agora em português (é o DEFAULT_LOCALE trabalhando):

A tela Olá, Ana Exemplo com os detalhes da conta: função Founder/CEO, empresa Loja Exemplo, site www.exemplo.com, idioma Português Brasileiro, fuso America/Sao Paulo, setor Retail and E-commerce, tamanho 1 a 10 e origem Google, com o botão Continuar

E aqui eu empaquei de novo. 😅 Cliquei em Continuar e nada aconteceu. Nenhuma mensagem, nenhum campo vermelho. O motivo: o botão só funciona com o Setor escolhido e com o Site preenchido, e o Site parece preenchido, porque mostra www.example.com em cinza. Isso é só o exemplo. Clique no lápis ao lado dele e digite o endereço do seu site de verdade. Aí o Continuar funciona.

🏠 O painel

E o painel aparece, ainda vazio:

O painel do Chatwoot da Loja Exemplo com o menu lateral (Caixa de Entrada, Conversas, Contatos, Relatórios, Campanhas, Central de Ajuda, Configurações), a lista de conversas vazia e os cartões de boas-vindas

O menu da esquerda é onde você vai passar o dia: Conversas é a caixa de entrada de tudo, Contatos são os clientes que já falaram com você, Relatórios mostram o tempo de resposta da equipe, e Configurações é onde se conectam os canais e se convidam os agentes. A lista do meio está vazia porque ainda não existe nenhum canal. Vamos criar o primeiro.

🧩 A primeira caixa de entrada: o chat do site

Em Configurações, Caixas de Entrada, clique em Adicionar Caixa de Entrada. O Chatwoot mostra os canais disponíveis:

A tela Escolha o Canal do Chatwoot com os cartões Site, Facebook, WhatsApp, SMS, E-Mail, API, Telegram, Line e Instagram, com Facebook e Instagram apagados

Escolha Site. Repare que Facebook e Instagram estão apagados: eles só ficam disponíveis depois que você cadastra um aplicativo na Meta e coloca as chaves no .env. Isso é assunto para outro dia.

O formulário do canal do site pede pouca coisa:

O formulário Canal do website preenchido com o nome Loja Exemplo, o domínio loja.exemplo.com, a cor do widget, a saudação Oi! Que bom te ver por aqui e a frase Respondemos em poucos minutos, de segunda a sexta
  • Nome do site: aparece no topo do chat, para o cliente.
  • Domínio do website: o site onde o chat vai ficar.
  • Seja bem-vindo e Bem-vindo, saudação: o título e a frase que o cliente vê ao abrir o chat.

Clique em Criar caixa de entrada. O passo seguinte pede os agentes, e ele tem uma frase que vale ler: "Como administrador, se você precisar acessar todas as caixas de entrada, adicione-se como agente". Ou seja, ser administrador não faz você receber as conversas. Escolha o seu próprio nome na lista e clique em Adicionar agentes.

A última tela entrega o que interessa: o código do chat para colar no site.

A tela Sua caixa de entrada está pronta, com o código script do widget do Chatwoot contendo BASE_URL e websiteToken, e os botões Mais configurações e Leva-me lá

Repare que o BASE_URL é exatamente o FRONTEND_URL do .env. É por isso que ele precisava ser o endereço final, com HTTPS. O websiteToken identifica esta caixa de entrada.

🛍️ Colando o chat no site

Clique em Copiar e cole o código no HTML do seu site, logo antes do </body>. Para testar, montei a página mais simples possível de uma loja de mentirinha:

<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<title>Loja Exemplo</title>
</head>
<body>
<h1>Loja Exemplo</h1>
<p>Caneca Arco-íris, 350 ml. Ficou com dúvida? Clique no balão azul.</p>

<!-- Cole aqui, antes do </body>, o código que o Chatwoot mostrou -->
<script>
  (function(d,t) {
    var BASE_URL="https://chat.seudominio.com.br";
    var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
    g.src=BASE_URL+"/packs/js/sdk.js";
    g.async = true;
    s.parentNode.insertBefore(g,s);
    g.onload=function(){
      window.chatwootSDK.run({
        websiteToken: 'SEU_TOKEN_AQUI',
        baseUrl: BASE_URL
      })
    }
  })(document,"script");
</script>
</body>
</html>

Abrindo a página, aparece o balão azul no canto de baixo. Fiz o papel de cliente: cliquei, comecei a conversa e perguntei sobre o frete. O Chatwoot respondeu na hora, sozinho, pedindo um e-mail de contato (é o robô dele, pedindo uma forma de contato para a equipe conseguir responder mesmo que o cliente feche a página). Deixei [email protected].

🙋 Atendendo o primeiro cliente

Do outro lado, no painel, a conversa apareceu na lista sem eu recarregar nada, e o Chatwoot já a atribuiu a mim (é a "Política Padrão" da distribuição automática). O contato ganhou o nome cliente, tirado do e-mail. Abri a conversa, escrevi a resposta e cliquei em Enviar:

A conversa aberta no painel do Chatwoot: a pergunta do cliente sobre a caneca para Curitiba, as mensagens automáticas pedindo o e-mail, o e-mail cliente@exemplo.com, o aviso Atribuído a Ana Exemplo por Política Padrão e a resposta da atendente sobre o prazo de 3 dias úteis

Repare nas duas abas em cima da caixa de texto: Responder manda para o cliente, e Mensagem Privada fica só entre a equipe, para combinar alguma coisa sem o cliente ver. Cuidado para não trocar as duas! 😅

E, no site, a resposta chegou no chat do cliente em segundos, também sem recarregar a página:

O chat do site da Loja Exemplo mostrando a pergunta do cliente, as mensagens do robô pedindo contato, o e-mail cliente@exemplo.com e a resposta da Ana Exemplo sobre o prazo para Curitiba

🔌 As três linhas do Nginx que parecem enfeite

Lembra das três linhas com o comentário "sem estas três linhas"? Eu quis ver com os meus olhos o que acontece sem elas. Coloquei um # na frente de cada uma (o # transforma a linha em comentário, e o Nginx passa a ignorá-la), recarreguei o Nginx e repeti a conversa inteira do zero, com um cliente novo.

O resultado, do lado do cliente:

O chat do site sem suporte a WebSocket no Nginx: só a pergunta do cliente aparece, sem as mensagens do robô, sem a resposta da atendente e sem a bolinha verde de online ao lado de Loja Exemplo

A pergunta foi enviada, e no painel a conversa até apareceu, com as mensagens do robô registradas. Mas o chat do cliente ficou mudo: não mostrou o pedido de e-mail, não mostrou a resposta da atendente, e até a bolinha verde de "online" do lado do nome da loja sumiu. Para o cliente, ninguém respondeu. 😱

O motivo: tudo que chega "sozinho" na tela, sem recarregar, viaja por uma conexão chamada WebSocket, que fica aberta entre o navegador e o Chatwoot. Para o Nginx repassar esse tipo de conexão, ele precisa daquelas três linhas. Sem elas, o envio funciona (é uma requisição comum), mas o caminho de volta não existe. E nada avisa: não tem erro no painel, não tem erro no chat. Voltei as linhas, recarreguei o Nginx, e a resposta voltou a chegar em segundos.

💾 Os dados sobrevivem a um reinício?

O restart: always do docker-compose.yaml faz as caixinhas voltarem sozinhas se a VPS reiniciar, e o instalador do Docker já deixa o próprio Docker ligando junto com a máquina. Mas eu queria ter certeza de que conta, atendente e conversas não moram dentro das caixinhas, e sim nos "discos" (volumes) que sobrevivem a elas. Derrubei tudo e subi de novo:

docker compose down
docker compose up -d

E perguntei ao Chatwoot o que ele tinha guardado, com o rails runner, que executa um comando dentro dele:

docker compose exec -T rails bundle exec rails runner 'puts "contas: #{Account.count}  usuarios: #{User.count}  caixas: #{Inbox.count}  conversas: #{Conversation.count}"'
contas: 1  usuarios: 1  caixas: 1  conversas: 1

Tudo lá. O login continuou funcionando e a conversa com o cliente estava no mesmo lugar. O que não sobrevive é o docker volume rm que usamos lá atrás, na tentativa que falhou: aquele comando apaga os dados de verdade, então nunca rode depois de o Chatwoot estar em uso.

Chatwoot no GitHubO código-fonte do projeto, com o .env.example e o docker-compose.production.yaml que usamos neste artigo.github.com

Por hoje é só, meus unicórnios! 🦄✨

Que a magia do arco-íris continue brilhando em suas vidas! Até mais! 🌈🌟

Perguntas frequentes

O Chatwoot é gratuito?
A versão que você instala no seu servidor é de código aberto e não cobra por agente nem por conversa. Você paga só a VPS. Na minha, os quatro contêineres somaram perto de 1,2 GB de memória, e a imagem principal ocupa 2,71 GB de disco.
Por que o db:chatwoot_prepare fica parado em "postgres:5432 - no response"?
Porque a senha do Postgres ficou vazia no docker-compose.yaml. O arquivo oficial tem a linha - POSTGRES_PASSWORD= sem valor, e o Postgres se recusa a iniciar sem senha. O comando fica esperando para sempre, sem mostrar erro. Coloque no docker-compose.yaml a mesma senha que você escreveu no .env.
Preciso de um domínio para usar o Chatwoot?
Para usar de verdade, sim. O docker-compose.yaml oficial publica o Chatwoot só em 127.0.0.1:3000, e o chat do site roda no navegador do seu cliente, que precisa alcançar o servidor por HTTPS. O caminho é apontar um subdomínio para a VPS e colocar o Nginx com um certificado do Let's Encrypt na frente.
Por que a resposta do atendente não aparece no chat do site?
Quase sempre é o Nginx sem suporte a WebSocket. Sem as linhas proxy_http_version 1.1, Upgrade e Connection "upgrade", o widget manda a mensagem, mas não recebe nada de volta em tempo real: nem a resposta do atendente, nem as mensagens automáticas.
Qualquer pessoa pode criar uma conta no meu Chatwoot?
Não, com o .env padrão. Ele vem com ENABLE_ACCOUNT_SIGNUP=false, e a rota de cadastro responde 404. O cuidado é outro: a tela de instalação fica aberta até alguém criar o primeiro administrador, então faça isso logo depois de ligar o HTTPS.
O botão Continuar da primeira tela não faz nada. O que falta?
Na tela de detalhes da conta, o botão só funciona com o campo Site e o Setor preenchidos, e ele não avisa. O Site parece preenchido porque mostra www.example.com, mas isso é só o exemplo: clique no lápis ao lado e digite o endereço.

Leia também