Saudações.
Resolvi criar esse artigo para documentar de maneira objetiva o funcionamento do RabbitMQ para operações em produção.
Pré-requisitos (constam em outros artigos aqui do blog):
- Instalação do Linux (Debian);
- Internet no servidor (sua VPS ou host);
- Docker CE instalado;
- Possuir um domínio de DNS (exemplo: seudominio.com.br);
- Possuir conhecimento básico de HTTP e uso do comando curl;
- Possuir conhecimento sobre o formato JSON;
O guia rápido do RabbitMQ focado na parte prática com exemplos está no link abaixo:
1 – O que é RabbitMQ
O RabbitMQ é um software que implementa o protocolo AMQP 0-9-1.
Explicação curta: O RabbitMQ é um broker AMQP.
Explicação longa: O RabbitMQ é um software que utiliza o protocolo AMQP (Advanced Message Queuing Protocol) para implementar uma mensageria. Ele atua na camada de aplicação como um servidor de mensagens assíncronas entre sistemas distribuídos. Ele atua como um intermediário (broker) que gerencia filas, roteamento e mensagens.
Diferenciais:
- Gratuito: não requer licença para uso comercial;
- Protocolos: oferece suporte a diferentes protocolos de mensageria:
- AMQP 0-9-1: protocolo principal;
- MQTT – Message Queuing Telemetry Transport: protocolo ideal para Internet das Coisas (IoT) e dispositivos com recursos limitados sendo muito comum em automação residencial e industrial;
- AMQP: suporte a AMQP 1.0 via plugins;
- STOMP – Simple Text Oriented Messaging Protocol: é um protocolo de mensagens simples e leve. Ele utiliza comandos fáceis (CONNECT, SEND, SUBSCRIBE) e funciona idealmente sobre WebSockets para comunicação em tempo real, mapeando mensagens para filas e exchanges;
- REST API: controle de configuração e objetos via HTTP POST/GET/PUT/DELETE;
- Administração: interface web simples e completa;
- Suporte a transações: garantia de armazenamento e entrega;
- Plugins: plugins podem ser instalados para adicionar recursos personalizados e expansão de protocolos;
- Observabilidade: logs e estatísticas para monitoramento.
Em termos práticos ele é um sistema de mensagens que funciona como um “WhatsApp para programas distribuídos conversarem entre si”.
1.1 – Engenharia de software, monolito e desacoplamento
Quando você faz um software com um código enorme que inclui centenas de bibliotecas e tem dezenas de módulos que são executados todos juntos em um único processo, isso é um monolito. Cada função dentro do software pode chamar outra função diretamente saltando para seu espaço na memória do processo.

Se esse software consumir uns 2 GB de memória RAM e consegue atender 5 usuários, pelas contas para atender 1.000 usuários você precisará de uns 20 TB de RAM, é muita coisa!
Outro problema grave de sistemas em monolito é a sequencia de workflow das tarefas internas. Uma loja virtual que depende de consulta em estoque, cobrança no cartão, emissão de nota fiscal e notificação por email para concluir a tarefa poderá falhar como um todo por estar executando todas as etapas em um único processo sequencial. Isso recebe o nome de “acoplamento forte“.
Para construir sistemas para muitos usuários (milhares a milhões) é impossível criá-lo como monolito pois não sabemos quanto de hardware cada setor/módulo do software vai consumir e quais serão os setores que mais vão trabalhar.
Para simplificar, cada pedaço do software (funções principais) são separados em serviços independentes. Isso se chama arquitetura de micros-serviços.
Para que esses serviços se comuniquem uma das técnicas é criar um HUB de mensagens para comunicação. O RabbitMQ é esse HUB.

Cada pedaço do software irá trabalhar em uma sequência específica de ENTRADA-PROCESSAMENTO-SAIDA, esse tipo de processo é chamado de worker (trabalhador).
Os workers podem ser replicados sob demanda. Se 2 workers dão conta do recado, ótimo, mas quando mensagens começam a acumular nas filas o gestor pode subir para 8 workers que irão balancear a carga de entrada para processar os trabalhos acumulados.
Essa arquitetura se chama “desacoplamento de sistema“. Esse é o papel do RabbitMQ em um ambiente de engenharia de software.
1.2 – Motivos para desacoplar
Exemplo – Sistema de assinatura digital:
- Um sistema faz uso de criptografia para assinatura digital e recebe uma mensagem de INPUT (argumento da função) para produzir uma OUTPUT assinada (retorno da função). O código que lida com a criptografia não precisa estar dentro do sistema principal (acoplamento forte, função de criptografia dentro do programa principal).
- Podemos colocar a função de criptografia em um programa independente que faça somente isso e possa rodar em outro servidor, longe do programa principal;
- A função se tornará um “serviço de assinatura digital”, esperando mensagens na fila “crypto-input” e colocando os trabalhos concluídos na fila “crypto-output“;
- Agora, nosso programa original precisará, em vez de chamar a função interna de criptografia, transformar os argumentos (documento a assinar) em um payload (JSON) e colocar na fila “crypto-input“;
- Para obter o documento assinado nosso programa principal precisará se conectar à fila “crypto-output” e esperar a resposta ficar pronta;
- Agora o ambiente possa escalar verticalmente e horizontalmente. Quanto mais mensagens precisarem ser assinadas mais softwares eu preciso rodar em paralelo para processar mensagens que aparecem na fila “crypto-input“.
Visão geral da função de assinatura digital transformada em serviço desacoplado de assinatura digital:

O ambiente completo pode ter servidores WEB que dependem de CPU e rede. Um sistema de arquivos que depende de espaço de armazenamento (I/O storage), outro sistema que faz a criptografia e assinatura digital depende de CPUs e outro que lida com registros em banco de dados depende de velocidade de armazenamento (I/O rápido NVME).
A implementação de filas entre esses serviços dá ao administrador as informações para decidir quais setores precisam de upgrade (mais paralelismo) para dar conta do trabalho acumulado e quais precisam de redução de instâncias por ociosidade para economizar recursos.

1.3 – Arquitetura do RabbitMQ
Internamente o RabbitMQ trabalha num fluxo linear e simples. O entendimento desses conceitos é vital para administrá-lo.
Glossário do RabbitMQ pela ordem de processamento:
- PRODUCER (produtores de mensagens): São os sistemas remetentes (softwares) que enviam mensagens o MQ. O ato de enviar é um PUBLISH (publicação);
- MESSAGE (mensagem): É o documento, normalmente no formato JSON, que você deseja submeter ao MQ. Ela é composta de:
- Atributos: parâmetros que manipulam o tratamento pelo MQ;
- Payload: conteúdo transportado pelo MQ da aplicação que enviou a mensagem até a aplicação que irá processá-la;
- EXCHANGE (caixa de entrada): A mensagem enviada de um producer para o MQ é recebida na porta 5672/TCP e depositada em uma caixa de entrada nomeada. Cada caixa deve possuir um nome único e um tipo específico que determina o método de distribuição (fanout, direct, topic, headers). O modo de persistência depende da propriedade “durable“:
- durable=true: as mensagens são escritas em disco antes de serem confirmadas como aceitas, isso garante que não haverá perda de mensagens;
- durable=false: as mensagens são armazenadas apenas na memória RAM, se houver falha do MQ ou desligamento do container/servidor mensagens podem ser perdidas.
- BINDING (roteamento de escuta): São conexões internas entre a caixa de entrada e a fila de mensagens. Sempre que uma mensagem é depositada na caixa, ela percorre todas as escutas (bindings) vinculadas a ela distribuindo cópias da mensagem para as escutas. Caberá à regra de escuta entregar ou não a cópia na fila. Após percorrer todas as bindings vinculadas a caixa descarta a mensagem inicial. Somente as cópias entregues nas filas permanecem até serem coletadas pelo consumidor;
- ROUTING KEY (chave de roteamento): é um atributo da mensagem que pode ser usado pela escuta para decidir se entrega ou não a mensagem a uma fila;
- HEADERS (cabeçalhos):
- QUEUE (fila de trabalhos acumulados): É onde as mensagens chegam em seu ponto de armazenamento final. Elas vão se acumulando a espera de algum consumidor. Filas também possuem o atributo de persistência “durable” (true ou false);
- CONSUMER (consumidores de mensagens): São sistemas destinatários que precisam coletar trabalho nas filas para processá-las. Ao conectar no MQ o consumidor envia o comando SUBSCRIBE (inscrição) para receber mensagens na fila desejada;
- ACK (aceitação): Um consumidor da fila recolhe uma mensagem e confirma ao MQ que ela foi devidamente processada resultando na remoção da mensagem. Se um consumidor não der ACK na mensagem ela será redisponibilizada para outro consumidor.
Para ficar claro o fluxo de mensagens, observe os diagramas.
O producer publicou (PUBLISH) uma nova mensagem na EXCHANGE (ela precisa ter um nome):

Ao detectar uma nova mensagem na entrada (EXCHANGE), as regras de escuta e distribuição (BINDING) conduzirão a mensagem até a fila de armazenamento (QUEUE).

As mensagens se acumulam na fila. O RabbitMQ espera que sistemas se conectem na porta 5672/TCP por meio do comando de inscrição (SUBSCRIBE).

Na visão geral o RabbitMQ é um sistema simples com poucas partes. Entendendo e praticando você se torna especialista muito rápido.
Visão geral:

1.4 – Suporte a virtualização
O RabbitMQ possui um conceito de virtualização chamado de VHOST (virtual host) que facilita a organização de diferentes sistemas em uma única instância.
O vhost padrão se chama “/” (barra), eu pessoalmente altero para “default” por meio da variável de ambiente RABBITMQ_DEFAULT_VHOST:

Uma empresa que construa um super servidor RabbitMQ de alta performance pode criar um VHOST para cada sistema cliente, exemplos:
- O vhost chamado “default” é o meu padrão inicial personalizado;
- Um vhost chamado “n8ntests” para servir às mensagens de sistemas de automações de teste do n8n (ferramenta de fluxo nocode).
Fluxo com VHOSTS:

Obviamente, uma exchange dentro da vhost “default” não aceita bindings para queues em outra VHOST – o isolamento é completo.
1.5 – Tipos de exchanges
Quando uma mensagem é publicada na exchange, o tipo da exchange define o fluxo a ser seguido para transferi-la para a queue.
Esse processo se chama roteamento e pode ser influenciado pela própria mensagem (atributos routing key ou headers).
Resumidamente os tipos de exchange são:
- fanout: Tipo de binding que ignora a routing key e entrega a mensagem para todas as filas vinculadas à exchange (broadcast de mensagens);
- direct: Entrega por routing key de binding exatamente igual à routing key da mensagem;
- topic: Roteia mensagens com base em padrões (patterns) do binding que são comparados com o routing key da mensagem;
- headers: Ignora a routing key. Faz o roteamento com base nos headers (cabeçalhos
x-match) da mensagem;
Vou explicar cada tipo com mais detalhes e exemplos.
1.5.1 – Exchange fanout
A exchange do tipo fanout (“Fan-out” ou expansão) é simples e pontual: apenas copia a mensagem da exchange para a fila sem nenhum critério de filtro na binding. Esse tipo é usado para entregar uma mensagem para uma ou várias filas diretamente como um broadcast.
Exemplo: um sistema de loja online. Sempre que uma nova compra acontece uma mensagem é enviada para a exchange “shopping” que entrega cópias nas filas:
- nfe: fila para o consumidor responsável por gerar nota fiscal eletrônica;
- smail: fila para o consumidor responsável por enviar um email confirmando a compra;
- stock: fila para o consumidor responsável pelo estoque afim de reservar produto para o cliente e posteriormente acionar o envio ao endereço de entrega.

1.5.2 – Exchange direct
Na exchange do tipo direct o binding é configurado com a palavra exata para comparar com o routing key na mensagem, logo o routing key da mensagem deve ser igual ao routing key da mensagem para que ela seja copiada para a fila.
Exemplo: um sistema de logs envia mensagens com as chaves “error“, “warning” ou “info“. Veja como fica a configuração do MQ:

O cenário acima é vital para entender a relação entre a exchange e as filas na distribuição interna dos bindings, principalmente pelo efeito de duplicação das mensagens criado pela binding em uma fila – várias bindings para uma fila.
1.5.3 – Exchange topic
O tipo topic é uma versão flexível do tipo direct. Em vez de fazer a comparação exata da routing key o tipo topic faz uma busca mágica baseada em padrões (patterns).
Apenas 2 caracteres são usados no padrão de busca do binding:
- Caracter coringa * (asterisco): substitui exatamente uma palavra na posição utilizada;
- Caracter # (cerquilha): substitui zero ou mais palavras na posição utilizada;
Para facilitar a filtragem das mensagens é recomendado que a chave seja escrita no formato Reverse Domain Name Notation (notação de nome de domínio reverso), também conhecido como reverse-DNS ou rDNS notation. Existe também a notação do OID (Object Identifier, ITU-T X.660 e ISO/IEC 9834-1).
A regra geral é seguir do alto nível para o mais específico separando por pontos:
- ALTO.MEDIO.BAIXO.ESPECIFICO.ID
Exemplos práticos:
- Sintaxe rDNS notation:
- br.com.patrickbrandao.blog.rabbitmq.comment (novo comentário no artigo);
- org.opencontainers.image.authors
- Sintaxe OID (tronco, galho principal, galho menor … folha):
1.3.6.1.4.1.311significa:1— ISO3— org (identified-organization)6— dod (US Department of Defense)1— internet4— private1— enterprise311— Microsoft
- Sintaxe genérica SISTEMA.SERVICO.FUNCAO.ID, exemplos:
- erp.authentication.check-login-status
- sensor.temperatura.carburador.1
- sensor.temperatura.ar-condicionado.14
A sintaxe permite abstrair o roteamento de mensagens dentro RabbitMQ em uma única exchange em vez de criar muitas exchanges e gerir quais softwares usarão cada uma.
Todos os sistemas farão postagem das mensagens em uma exchange principal e o engenheiro fará a distribuição das mensagens para filas específicas. Considere:
- Vantagens: softwares mais simples, basta conectar no MQ e despejar tudo em uma única exchange;
- Desvantagens: o administrador não pode errar na hora de criar dezenas, centenas e até milhares de bindings para filas específicas. A engenharia do fluxo passa a ser feita no RabbitMQ.
Agora que temos um padrão hierárquico podemos aplicar os coringas (* e #) assim:
- A busca
org.company.*.logirá capturar apenas uma palavra entre “org.company” e “log”:- Vai pegar:
org.company.auth.logeorg.company.logout.log; - Não vai pegar:
org.company.auth.log.error;
- Vai pegar:
- A busca
org.company.#irá capturar TUDO que começa com “org.company” independente da profundidade:- Vai pegar:
org.company.logout.message - Vai pegar:
org.company.auth.log.info
- Vai pegar:
- A busca
sensor.temperatura.#irá capturar todos os sensores de temperatura; - A busca
sensor.irá capturar todos os tipos de sensores da sala;*.sala.*
Resumindo, o asterisco (*) pega uma palavra exata no local de busca preservando a exatidão anterior e posterior. A cerquilha (#) é extremamente generalista e pega tudo que estiver no seu lado de busca.
Design:

1.5.4 – Exchange headers
O tipo headers roteia mensagens com base nos cabeçalhos (headers) da mensagem.
Esse tipo ignora completamente a routing key. É o tipo mais flexível pois permite roteamento baseado em múltiplos atributos arbitrários definidos na binding.
Ao criar o binding entre a exchange e a fila você define um conjunto de pares chave-valor como argumentos do binding. Quando uma mensagem chega ao exchange, o RabbitMQ compara os headers da mensagem com os argumentos de cada binding para decidir para qual fila encaminhar.
Cada binding possui um argumento obrigatório chamado x-match, que define a lógica de correspondência:
- x-match all: Todos os pares chave-valor na binding devem estar presentes e com valores iguais nos headers da mensagem (lógica AND, todos);
- x-match any: Pelo menos um par chave-valor na binding deve corresponder nos headers da mensagem (lógica OR, algum);
Ao criar um binding com estes argumentos:
x-match = all
formato = pdf
origem = financeiroUma mensagem so sera roteada para essa fila se seus headers contiverem ambos: formato=pdf e origem=financeiro.
Se o x-match fosse any, bastaria conter um dos dois.
Esse tipo é especialmente preferido pois obriga os sistemas que enviam as mensagens a obedecerem um padrão claro de detalhamento de metadados (cabeçalhos) da mensagem.
1.6 – Consumidores
Os consumidores participam da parte mais fácil: se conectam em uma fila (SUBSCRIBE) e aguardam mensagens para processar.
Um detalhe importante sobre as regras de entrega final da mensagem é que a fila não duplica mensagens aos consumidores de uma fila.
Dois consumidores conectados a uma fila chamada “auth-logs” receberão de maneira balanceada as mensagens que forem chegando na fila (10 mensagens => 2c => 5 para cada consumidor).
Quando um consumidor puxa a mensagem a fila não a deleta imediatamente. O consumidor recebe a cópia e a fila registra internamente que a mensagem foi entregue para aquela conexão específica. O consumidor terá seu tempo para processar o payload da mensagem e enviar o ACK para confirmar à fila que a tarefa foi concluída com sucesso.
Sem o ACK a fila considera que o consumidor falhou ao processá-la. A mensagem será restaurada à fila e entregue ao próximo consumidor (mesmo que seja o mesmo que falhou anteriormente).
A mensagem é retornada ao consumidor em forma de lista JSON , possuindo um mínimo de 1 objeto. Exemplo de descarregamento da fila com 2 mensagens:
[
{
"payload_bytes": 55,
"redelivered": false,
"exchange": "inbox_fanout",
"routing_key": "nao-importa-2",
"message_count": 3,
"properties": [],
"payload": "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
"payload_encoding": "string"
},
{
"payload_bytes": 38,
"redelivered": false,
"exchange": "inbox_fanout",
"routing_key": "tanto-faz",
"message_count": 0,
"properties": [],
"payload": "Duis aute irure dolor in reprehenderit",
"payload_encoding": "string"
}
]1.7 – A mensagem
A mensagem é o objeto que dá sentido a estrutura do RabbitMQ. Embora suporte vários formatos, é mais comum e proliferado o uso do JSON.
A mensagem tem uma estrutura JSON (payload enviado para a porta API ou porta AMQP) e carregam um payload que pode ser de qualquer tipo (JSON, binário, XML, texto, …);
JSON minimalista (transportando payload entre APPs em formato JSON):
{
"routing_key": "chave-de-roteamento",
"payload": "{\n\t\"event\": \"sold\",\t\"id\": 42,\t\"valor\": 199.90\n}",
"payload_encoding": "string",
"properties": {}
}JSON completo estruturado:
{
"routing_key": "minha.chave",
"payload": "{\n\t\"event\": \"sold\",\t\"id\": 42,\t\"valor\": 199.90\n}",
"payload_encoding": "string",
"properties": {
"content_type": "application/json",
"content_encoding": "utf-8",
"delivery_mode": 2,
"priority": 5,
"correlation_id": "req-abc-123",
"reply_to": "fila.de.resposta",
"expiration": "60000",
"message_id": "msg-uuid-9999",
"timestamp": 1700000000,
"type": "pedido.criado",
"user_id": "vsite_admin",
"app_id": "minha-aplicacao",
"headers": {
"x-origem": "checkout",
"x-versao": "2"
}
}
}As propriedades “properties” da mensagem são importantes para que a mensagem seja roteada e tratada corretamente. Detalhamento dos campos:
| Campo | Tipo | Descrição |
|---|---|---|
content_type | string | MIME type do payload |
content_encoding | string | Encoding do conteúdo (ex: utf-8, gzip) |
delivery_mode | int | 1 = não persistente, 2 = persistente (sobrevive a restart) |
priority | int | Prioridade de 0 a 9 |
correlation_id | string | ID para correlacionar com uma requisição/resposta |
reply_to | string | Fila para onde a resposta deve ser enviada |
expiration | string | TTL em milissegundos (como string) |
message_id | string | Identificador único da mensagem |
timestamp | int | Unix timestamp de criação |
type | string | Tipo/evento da mensagem (convenção da aplicação) |
user_id | string | Deve bater com o usuário autenticado no RabbitMQ |
app_id | string | Identificador da aplicação produtora |
headers | object | Headers customizados arbitrários |
1.8 – Segurança
Portas utilizadas para comunicação IP (IPv4 ou IPv6):
- 4369 (EPMD): Erlang Port Mapper Daemon. É usado para descoberta de nós em clusters RabbitMQ. Os nós Erlang usam essa porta para se encontrarem. Você só precisa dela se estiver montando um cluster com múltiplos nós.
- 5672 (AMQP): Porta principal do protocolo AMQP 0-9-1 (e também AMQP 1.0 no RabbitMQ 4.x). É por aqui que suas aplicações se conectam para publicar e consumir mensagens.
- 15672 (Management UI): Interface web de gerenciamento e API HTTP. É o painel web onde você visualiza filas, exchanges, conexões, etc.
- 15692 (Prometheus): Endpoint de métricas no formato Prometheus (
/metrics). Só precisa expor se estiver coletando métricas com Prometheus/Grafana; - 25672 (Erlang Distribution): Porta usada para comunicação inter-nós no cluster Erlang (é a 20000 + a porta AMQP). Assim como a 4369, só é necessária em cenários de cluster.
As portas do RabbitMQ não possuiem criptografia TLS/SSL por padrão. O tráfego na porta AMQP 5672/tcp é texto plano puro (senha passa aberta) e não deve cruzar a Internet.
Pontos a considerar para a segurança:
- Criptografia HTTPs: A transmissão pela Internet deve ser protegida por TLS/SSL, a forma mais simples de fazer isso é usando um proxy-reverso provendo HTTPs para a API REST;
- VPN: ambientes distribuídos em vários servidores devem provisionar a comunicação IP segura entre eles;
- Autenticação: mesmo com criptografia a autenticação pode ser um ponto explorável, os erros comuns são vazamento de credenciais e utilização de senhas fracas;
- Firewall: impedir que qualquer IP tenha acesso às portas e à URL do RabbitMQ é repreensível, falhas não documentadas podem dar acesso total ao invasor;
- Atualização: garantir que atualizações sejam testadas e aplicadas constantemente.
Cada porta aberta resulta numa superfície de ataque:
- Porta 4369 – EPMD (Erlang Port Mapper Daemon): O atacante pode enumerar seus nós e se ele obter o cookie Erlang ele poderá realizar execuções remotas (RCE);
- Porta 5672 – AMQP (protocolo principal): Porta de produção/consumo de mensagens. Sujeita a ataques de força bruta contra credenciais, e o usuário padrão guest/guest (normalmente restrito a conexões localhost) permite acesso total;
- Porta 15672 – Management UI (HTTP API): Alvo trivial para scanners automatizados e força bruta contra credenciais;
- Porta 15692 – Prometheus metrics: Expõe métricas internas, quantidade de mensagens, conexões, uso de memória, disco. Facilita reconhecimento do ambiente por um atacante;
- Porta 25672 – Erlang distribution (inter-node): Comunicação entre nós do cluster. Se o atacante obtiver o cookie Erlang ele ganha shell remoto no container.
Não publicar nenhuma porta no Docker e entregar o acesso à porta HTTP 15672 para o Traefik (com ACL) é a maneira mais restrita de usá-lo com segurança.
Você pode criar o arquivo de configuração “/etc/rabbitmq/rabbitmq.conf” e montá-lo em um volume no container e adicionar mais pontos de segurança. Também é possível abrir a porta do AMQP com SSL/TLS diretamente com certificados próprios.
2 – Preparando o Docker
Vamos preparar o ambiente docker com a rede (network_public), o Traefik com suporte a repasse do tráfego AMQP com TLS e suporte HTTPs via LetsEncrypt.
2.1 – Docker Network
Criando a rede network_public (escolha ipv4-only ou dual-stack):
# Rede de containers
# - Versão padrão IPv4 only
docker network create \
-d bridge \
\
-o "com.docker.network.bridge.name"="br-net-public" \
-o "com.docker.network.bridge.enable_icc"="true" \
-o "com.docker.network.driver.mtu"="65495" \
\
--subnet 10.249.0.0/16 --gateway 10.249.255.254 \
\
network_public;
# - Versão dual-stack IPv4 + IPv6
docker network create \
-d bridge \
\
-o "com.docker.network.bridge.name"="br-net-public" \
-o "com.docker.network.bridge.enable_icc"="true" \
-o "com.docker.network.driver.mtu"="65495" \
\
--subnet 10.249.0.0/16 --gateway 10.249.255.254 \
--ipv6 \
--subnet=2001:db8:10:249::/64 \
--gateway=2001:db8:10:249::ffff \
\
network_public;
2.2 – Container do Traefik
Alterações feitas na configuração do container Traefik:
- Redirecionamento da porta 5671/tcp do host para o container;
- Adicionei na configuração do Traefik uma nova “entrypoint” chamada “amqps” (AMQP Seguro) nessa porta 5671/tcp;
O cliente se conectará usando o protocolo AMQP com TLS na porta 5671/tcp já com certificado assinado pelo LetsEncrypt (reconhecido globalmente) e o Traefik repassará a conexão sem TLS à porta AMQP do container RabbitMQ.
Essa técnica garante criptografia ao atravessar a Internet usando o Traefik como TLS offloading. O RabbitMQ suporta TLS nativo mas não vou abordar isso aqui.
O container do Traefik se chamará “traefik-app“. Personalize a variável EMAIL para que o LetsEncrypt funcione corretamente. Execute:
#!/bin/sh
# Variaveis
# - Nome do container
NAME=traefik-app;
# - Imagem do software traefik
IMAGE=traefik:latest;
# - Email de registro no letsencrypt
EMAIL="voce@seudominio.com.br";
# - Diretorio do volume (dados persistentes)
DATADIR=/storage/$NAME;
# Preparar volume:
mkdir -p $DATADIR/letsencrypt;
mkdir -p $DATADIR/logs;
mkdir -p $DATADIR/config;
# Obter imagem atualizada:
docker pull $IMAGE;
# Remover instância atual:
docker rm -f $NAME 2>/dev/null;
# Renovar/criar/rodar:
docker run \
-d --restart=unless-stopped --name $NAME -h $NAME.intranet.br \
\
--network network_public \
\
-p 80:80 \
-p 443:443 \
-p 5671:5671 \
\
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v $DATADIR/letsencrypt:/etc/letsencrypt \
-v $DATADIR/config:/etc/traefik \
-v $DATADIR/logs:/logs \
\
--tmpfs /run:rw,noexec,nosuid,size=16m \
--tmpfs /tmp:rw,noexec,nosuid,size=16m \
--read-only \
\
$IMAGE \
\
--global.checkNewVersion=false \
--global.sendAnonymousUsage=false \
\
--api.insecure=true \
\
--log.level=INFO \
--log.filePath=/logs/error.log \
\
--accessLog.filePath=/logs/access.log \
\
--entrypoints.web.address=:80 \
--entrypoints.web.http.redirections.entryPoint.to=websecure \
--entrypoints.web.http.redirections.entryPoint.scheme=https \
--entrypoints.web.http.redirections.entryPoint.permanent=true \
\
--entrypoints.websecure.address=:443 \
\
--entrypoints.amqps.address=:5671 \
\
--providers.docker=true \
--providers.file.directory=/etc/traefik \
\
--certificatesresolvers.letsencrypt.acme.email=$EMAIL \
--certificatesresolvers.letsencrypt.acme.storage=/etc/letsencrypt/acme.json \
--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web;
3 – Implementando RabbitMQ no Docker
Vamos criar o container do MQ com atenção a detalhes e escolhas que você deve fazer.
3.1 – Nome de DNS para acesso seguro HTTPs / TLS
Seu servidor precisa ter IP público e um nome de DNS para que o LetsEncrypt possa fornecer ao Traefik um certificado assinado. Esse certificado é vital para o acesso HTTPs à administração e também a API REST (https://rabbitmq.seudomino.com.br/…);
Configure no seu DNS seguindo o exemplo:
- IPv4: rabbitmq.seudomino.com.br registro tipo A valor 45.255.128.2 (IPv4 da VPS);
- IPv6: rabbitmq.seudomino.com.br registro tipo AAAA valor 2001:abcd::2 (IPv6 da VPS);
- Crie o registro tipo AAAA somente se sua VPS possuir IPv6 global fixo;
Neste tutorial, troque “rabbitmq.seudominio.com.br” para seu nome de DNS (FQDN);
Esse nome será usado pelo protocolo HTTP (porta 80/tcp) e HTTPS (porta 443/tcp) para conectar ao Traefik (Proxy-Reverso) e conduzirá as conexões até o container “rabbitmq” na porta 15672/TCP (porta de servidor HTTP do MQ).
3.2 – Acesso AMQP direto
Se for importante para você fazer o acesso sem criptografia direto na porta AMQP 5672/TCP, o redirecionamento é feito no argumento “-p 5672:5672” e só apresenta risco se segurança se você usar passando por uma rede IP suspeita.

3.3 – Portas não publicadas
Não farei o redirecionamento das portas 4369/TCP, 15692/TCP e 25672/TCP pois não vou abordar cluster nesse tutorial. Sinta-se a vontade em publicá-las se precisar.
Não farei o redirecionamento da porta 15672/TCP (administração web http pura) pois o acesso a ele ocorrerá por HTTPs por meio do Traefik.
3.4 – Volumes do container RabbitMQ
O volume serve para armazenar mensagens persistentes que resistem a reinicialização do container “rabbitmq” ou até mesmo do desligamento do servidor inteiro (host, Docker).
Detalhes do volume:
- Pasta interna: Diretório dentro do container fica em “/var/lib/rabbitmq“;
- Pasta externa do volume: Volume mapeado no host em “/storage/rabbitmq“;
3.5 – Cookie Erlang
O cookie Erlang é utilizado para que as ferramentas de linha de comando (rabbitmqctl, rabbitmq-diagnostics, rabbitmq-plugins) se conectam ao no RabbitMQ via protocolo de distribuição Erlang, alem de ser vital em ambientes em cluster.
O cookie é especificado na variável de ambiente RABBITMQ_ERLANG_COOKIE e deve ser constituido de uma palavra-chave de alta entropia representado em ASCII hexadecimal.
Exemplo de comando para gerar cookie seguro:
# Gerar sequencia hexadecimal para cookie seguro:
openssl rand -hex 32;
# Saida de exemplo:
# ce97851904e9d9663116eee446d2dc19fa77808681c5bd8770498b6b05cb7c0f
Vou criar um arquivo na pasta do volume para que esse valor permaneça igual entre todas as execuções, script:
# Variáveis
# - Pasta do volume
DATADIR=/storage/rabbitmq;
# Garantir existencia da pasta do volume
mkdir -p $DATADIR;
# Se o arquivo de token salvo não existir, criar chave randomica de 256 bits
if [ -s "$DATADIR/.cookie" ]; then
# Arquivo presente e preenchido
echo "# Cookie presente, usando valor persistente";
else
# Arquivo ausente ou vazio
echo "# Cookie AUSENTE, gerando nova chave";
KEY=$(openssl rand -hex 32);
echo "$KEY" > "$DATADIR/.cookie";
fi;
# Preenchendo variável de ambiente com valor de cookie persistente:
export RABBITMQ_ERLANG_COOKIE=$(head -1 $DATADIR/.cookie);
3.6 – Criando e rodando o container do RabbitMQ
Personalize as variáveis para valores adequados a cada função descrita nos comentários.
- Variável RABBITMQ_DEFAULT_VHOST: O vhost padrão é “/” mas alterei para “default“;
- Altere a senha do usuário admin em RABBITMQ_DEFAULT_PASS;
- Os parâmetros “cpus” e “mem_limit” devem ser personalizados para montar um ambiente crítico (acima de milhares de mensagens);
Script em SHELL (para rodar como root):
# Variaveis
# - Nome do container
NAME="rabbitmq";
# - Nome de DNS para acesso pelo navegador
FQDN="rabbitmq.seudominio.com.br";
# - Timezone para hora local
TZ="America/Sao_Paulo";
# - VHOST padrão (alternativo ao "/")
RABBITMQ_DEFAULT_VHOST="default";
# - Troque por uma sequencia hexadecimal aleatoria
RABBITMQ_ERLANG_COOKIE=$(head -1 $DATADIR/.cookie);
# - Cookie padrão em caso de ausencia do arquivo acima
[ "x$RABBITMQ_ERLANG_COOKIE" = "x" ] && \
RABBITMQ_ERLANG_COOKIE="ffffffffffffffffffffffffffffffff";
# - Credenciais de acesso administrativo inicial
RABBITMQ_USER="admin";
RABBITMQ_PASS="Sua_SenhaForte@";
# - Pasta do volume
DATADIR=/storage/rabbitmq;
#IMAGE=rabbitmq:management;
IMAGE="rabbitmq:4-management";
# - Limite de recursos
CPU_LIMIT="2.0";
RAM_LIMIT="2g";
# Criar pasta do volume
mkdir -p $DATADIR;
# Parar container atual:
docker rm -f $NAME 2>/dev/null;
# Criar e rodar:
docker run -d --restart=always \
--name $NAME --hostname $NAME.intranet.br \
\
--cpus="$CPU_LIMIT" \
--memory=$RAM_LIMIT --memory-swap=$RAM_LIMIT \
\
--tmpfs /run:rw,noexec,nosuid,size=512m \
--tmpfs /tmp:rw,noexec,nosuid,size=512m \
\
--network network_public \
\
-p 5672:5672 \
\
-e TZ=$TZ \
-e RABBITMQ_DEFAULT_VHOST=$RABBITMQ_DEFAULT_VHOST \
-e RABBITMQ_ERLANG_COOKIE=$RABBITMQ_ERLANG_COOKIE \
-e RABBITMQ_DEFAULT_USER=$RABBITMQ_USER \
-e RABBITMQ_DEFAULT_PASS=$RABBITMQ_PASS \
\
-v $DATADIR:/var/lib/rabbitmq \
\
--label "traefik.enable=true" \
--label "traefik.http.routers.$NAME.rule=Host(\`$FQDN\`)" \
--label "traefik.http.routers.$NAME.entrypoints=web,websecure" \
--label "traefik.http.routers.$NAME.tls=true" \
--label "traefik.http.routers.$NAME.tls.certresolver=letsencrypt" \
--label "traefik.http.services.$NAME.loadbalancer.passHostHeader=true" \
--label "traefik.http.services.$NAME.loadbalancer.server.port=15672" \
\
--label "traefik.tcp.routers.$NAME-amqp.entrypoints=amqps" \
--label "traefik.tcp.routers.$NAME-amqp.rule=HostSNI(\`$FQDN\`)" \
--label "traefik.tcp.routers.$NAME-amqp.tls=true" \
--label "traefik.tcp.routers.$NAME-amqp.tls.certresolver=letsencrypt" \
--label "traefik.tcp.services.$NAME-amqp.loadbalancer.server.port=5672" \
\
--entrypoint "docker-entrypoint.sh" \
\
$IMAGE rabbitmq-server;
echo;
echo "Acesso:";
echo "Web......: https://$FQDN";
echo;
Se preferir usar docker compose:
services:
rabbitmq:
image: rabbitmq:4-management
container_name: rabbitmq
hostname: rabbitmq.intranet.br
restart: always
entrypoint: ["docker-entrypoint.sh", "rabbitmq-server"]
cpus: 2.0
mem_limit: 2g
memswap_limit: 2g
tmpfs:
- /run:rw,noexec,nosuid,size=512m
- /tmp:rw,noexec,nosuid,size=512m
ports:
- "5672:5672"
environment:
TZ: "America/Sao_Paulo"
RABBITMQ_ERLANG_COOKIE: "ffffffffffffffffffffffffffffffff"
RABBITMQ_DEFAULT_VHOST: "default"
RABBITMQ_DEFAULT_USER: "admin"
RABBITMQ_DEFAULT_PASS: "Sua_SenhaForte@"
volumes:
- /storage/rabbitmq:/var/lib/rabbitmq
networks:
- network_public
labels:
- "traefik.enable=true"
- "traefik.http.routers.rabbitmq.rule=Host(`rabbitmq.seudominio.com.br`)"
- "traefik.http.routers.rabbitmq.entrypoints=web,websecure"
- "traefik.http.routers.rabbitmq.tls=true"
- "traefik.http.routers.rabbitmq.tls.certresolver=letsencrypt"
- "traefik.http.services.rabbitmq.loadbalancer.passHostHeader=true"
- "traefik.http.services.rabbitmq.loadbalancer.server.port=15672"
- "traefik.tcp.routers.rabbitmq-s.entrypoints=amqps"
- "traefik.tcp.routers.rabbitmq-s.rule=HostSNI(`rabbitmq.seudominio.com.br`)"
- "traefik.tcp.routers.rabbitmq-s.tls=true"
- "traefik.tcp.routers.rabbitmq-s.tls.certresolver=letsencrypt"
- "traefik.tcp.services.rabbitmq-s.loadbalancer.server.port=5672"
networks:
network_public:
external: true3.7 – Ajustes de segurança
É muito importante remover o usuário guest:
# Nome do container
NAME="rabbitmq";
# Remover usuario guest:
docker exec $NAME rabbitmqctl delete_user guest;
Se precisar alterar a senha do usuário admin e garantir privilégios completos:
# Nome do container
NAME="rabbitmq";
# Alterar senha do admin:
docker exec $NAME rabbitmqctl change_password admin 'Sua_SenhaForte@';
# Garantir (reafirmar) poderes ao admin:
docker exec $NAME rabbitmqctl set_user_tags admin administrator;
docker exec $NAME rabbitmqctl set_permissions -p default admin ".*" ".*" ".*";
Criar um usuário “suporte” com poderes de admin:
# Nome do container
NAME="rabbitmq";
# Adicionar usuario suporte com todos os poderes de admin:
docker exec $NAME rabbitmqctl add_user suporte 'Sua_SenhaForte@';
docker exec $NAME rabbitmqctl set_user_tags suporte administrator;
docker exec $NAME rabbitmqctl set_permissions -p default suporte ".*" ".*" ".*";
Listar usuários:
# Nome do container
NAME="rabbitmq";
# Listar usuários:
docker exec $NAME rabbitmqctl list_users;
3.8 – Primeiro acesso web
O RabbitMQ pode ser totalmente administrado pela interface web!
Acesse a URL configurada na variável $FQDN do script acima para abrir no navegador.

Após logar, a interface web permite a criação de objetos (VHOSTs, exchanges, bindings, filas, novos usuários, controle de privilégio, logs, estatísticas, etc).

Vou me ater aos comandos e scripts pois eles facilitam o boot de ambientes automatizados e a criação automatizada de objetos.
4 – Comandos básicos de terminal
Esse capítulo aborda as operações de administração do MQ por meio do shell (terminal).
Se você estiver no terminal do servidor utilize sempre “docker exec …“.
Se estiver no shell dentro do container inicie os comandos em “rabbitmqctl …“.
Para entrar no terminal do container rabbitmq, execute:
# Nome do container
NAME="rabbitmq";
# Entrar no shell do container:
docker exec -it $NAME bash;
4.1 – Status e sanidade do ambiente
Esses comandos visam verificar se o RabbitMQ está rodando e operacional:
# Nome do container
NAME="rabbitmq";
# Entrar no shell do container:
docker exec -it $NAME bash;
# Comandos básicos
# Status geral
rabbitmqctl status; # Status do serviço:
rabbitmqctl ping; # Teste de funcionamento
rabbitmqctl environment; # Informações detalhadas
# Listar filas
rabbitmqctl list_queues;
# Listar exchanges
rabbitmqctl list_exchanges;
# Listar conexões
rabbitmqctl list_connections;
# Listar plugins habilitados
rabbitmq-plugins list --enabled;
# Health check manual
rabbitmq-diagnostics -q ping;
# Habilitar um plugin
#- docker exec $NAME rabbitmq-plugins enable <nome_do_plugin>;
4.2 – Operações básicas
Agora vamos manipular os objetos:
# Nome do container
NAME="rabbitmq";
# Entrar no shell do container:
docker exec -it $NAME bash;
# Comandos básicos de gestão de usuários:
# Listar usuários
rabbitmqctl list_users;
# Conferir permissões do usuário
rabbitmqctl list_user_permissions admin;
# Adicionar usuário
rabbitmqctl add_user user01 tulipa;
# Alterar senha do usuário
rabbitmqctl change_password user01 javali;
# Definir tag no usuário (administrator = super-poderes):
rabbitmqctl set_user_tags user01 administrator;
# Deletar usuário:
rabbitmqctl delete_user user01;
# Gerenciamento de Virtual Host (VHOST):
# Listar virtual hosts
rabbitmqctl list_vhosts;
# Criar virtual host
rabbitmqctl add_vhost n8ntests;
# Adicionar usuário para uso no VHOST:
rabbitmqctl add_user operador tulipa;
# Listar/Verificar usuários:
rabbitmqctl list_users;
# Definir permissões de um usuario no VHOST (configure, write, read)
rabbitmqctl set_permissions -p n8ntests operador ".*" ".*" ".*";
# Conferir permissões do usuário:
rabbitmqctl list_user_permissions operador;
# Retirar todas as permissões do usuário:
rabbitmqctl clear_permissions -p n8ntests operador
# Remover virtual host:
rabbitmqctl delete_vhost n8ntests;
# Listar/Verificar exchanges
rabbitmqctl list_exchanges -p default;
rabbitmqctl list_exchanges -p n8ntests;
# Listar/Verificar filas
rabbitmqctl list_queues -p default;
rabbitmqctl list_queues -p n8ntests;
# Listar/Verificar bindings
rabbitmqctl list_bindings -p default;
rabbitmqctl list_bindings -p n8ntests;
# Listar/Verificar canais ativos:
rabbitmqctl list_channels -p default;
rabbitmqctl list_channels -p n8ntests;
# Declarar exchange do tipo fanout para broadcast
rabbitmqctl -u operador -p tulipa -V n8ntests \
declare exchange name=broadcast_exchange type=fanout;
# Criar fila n8nq01:
rabbitmqctl -u operador -p tulipa -V n8ntests \
declare queue name=n8nq01 durable=true;
# Criar fila n8nq02:
rabbitmqctl -u operador -p tulipa -V n8ntests \
declare queue name=n8nq02 durable=true;
# Fazer o binding das filas com a exchange (obs: sem routing key para fanout):
# - binding broadcast_exchange->n8nq01
rabbitmqctl -u operador -p tulipa -V n8ntests \
declare binding source=broadcast_exchange destination=n8nq01;
# - binding broadcast_exchange->n8nq02
rabbitmqctl -u operador -p tulipa -V n8ntests \
declare binding source=broadcast_exchange destination=n8nq02;
# Conferir:
rabbitmqctl list_exchanges -p n8ntests;
rabbitmqctl list_queues -p n8ntests;
rabbitmqctl list_bindings -p n8ntests;
rabbitmqctl list_channels -p n8ntests;
# Conferir canais ativos:
rabbitmqctl list_channels -p n8ntests;
# Listing channels ...
# pid user consumer_count messages_unacknowledged
# <rabbit@rabbitmq.1754427035.64342.0> operador 1 0
# Desconectar canal:
rabbitmqctl close_connection \
"<rabbit@rabbitmq.1754427035.64342.0>" "Cai-cai-balao"
# Closing connection <rabbit@rabbitmq.1754427035.64342.0>, reason: Cai-cai-balao
# Listar filas com mais detalhes (default):
rabbitmqctl list_queues name messages consumers;
# Listar filas com mais detalhes no VHOST n8ntests:
rabbitmqctl list_queues -p n8ntests name messages consumers;
# Purgar uma fila (remover todas as mensagens):
rabbitmqctl purge_queue -p n8ntests n8nq01;
rabbitmqctl purge_queue -p n8ntests n8nq02;
# Parar aplicação RabbitMQ (mantém Erlang VM):
rabbitmqctl stop_app;
# Iniciar aplicação RabbitMQ:
rabbitmqctl start_app;
# Resetar node (remove dados):
rabbitmqctl reset;
# Forçar reset:
rabbitmqctl force_reset;
“Eu creio que um dos princípios essenciais
da sabedoria é o de se abster das
ameaças verbais ou insultos.“
Nicolau Maquiavel
Terminamos por hoje!
Patrick Brandão, patrickbrandao@gmail.com
