Como instalar o Discourse numa VPS com Docker
Aprenda a instalar o Discourse com o launcher Docker oficial, configurar RAM e swap, domínio, SMTP, app.yml, rebuild, TLS e reverse proxy.
Instalar o Discourse numa VPS: um contentor, um ficheiro de configuração
Para instalar o Discourse numa VPS, execute o instalador do próprio projeto, responda a um assistente curto e aguarde a compilação. O Discourse é distribuído como um único contentor Docker que contém a aplicação Rails, o PostgreSQL, o Redis e o nginx. Tudo o que alterar mais tarde fica no ficheiro /var/discourse/containers/app.yml, e cada alteração chega ao site através de uma nova compilação.
A instalação oficial é discourse_docker: um script de shell launcher e um conjunto de modelos YAML. O Discourse não suporta um ficheiro Compose criado por si, e o contentor não foi concebido para ser separado manualmente. Se está habituado a executar serviços numa VPS com Docker Compose, espere uma estrutura diferente. Aqui não existe docker compose up -d, e ./launcher rebuild app é a implementação.
O que o Discourse exige antes de começar
Quatro requisitos costumam causar problemas, e cada um deles bloqueia o processo antes de chegar à página de login.
- Memória. Um contentor executa o PostgreSQL, o Redis, o Sidekiq e um servidor web Ruby. A etapa de compilação dos recursos precisa de mais memória do que o site em execução.
- Um nome de domínio real. A configuração de exemplo fornecida é explícita: "O Discourse não funciona com um endereço IP isolado."
- Um caminho para envio de correio. A ativação de contas, as reposições de palavra-passe, os convites de administradores e as mensagens de resumo são enviados por SMTP (simple mail transfer protocol).
- As portas 80 e 443 livres no host, exceto se colocar deliberadamente o Discourse atrás de um proxy que já esteja em execução.
The data behind this chart
[
{
"label": "Documented minimum",
"ram_gb": 1,
"storage_gb": 10
},
{
"label": "Documented recommended",
"ram_gb": 2,
"storage_gb": 20
}
]O documento oficial de instalação define como mínimo 1 GB de RAM com swap e 10 GB de disco, e recomenda 2 GB de RAM com 20 GB de disco. Interprete a primeira linha como o valor que permite concluir o instalador, não como o valor pretendido para executar uma comunidade. A diferença é importante porque o pico de memória ocorre durante a compilação, não devido ao tráfego.
Aponte o domínio para o servidor antes de instalar
Crie um registo A para o nome de anfitrião que vai utilizar e confirme-o a partir do próprio servidor.
dig +short forum.example.com
curl -4 -s https://ifconfig.coOs dois comandos têm de apresentar o mesmo endereço. Isto é necessário porque o assistente de configuração testa a ligação ao seu nome de anfitrião, e um registo que ainda aponte para outro local falha nesse teste. Um registo criado há dois minutos também pode continuar em cache. Aguarde que o TTL (time to live) anterior expire em vez de tentar contornar o assistente.
Decida agora se o registo será colocado atrás de um CDN. Um registo com proxy oculta o endereço do servidor, e o pedido de certificado do contentor falha porque o desafio ACME (automatic certificate management environment) é respondido pelo proxy, e não pelo Discourse. Mantenha o registo sem proxy durante a primeira instalação.
Executar o instalador oficial
Um comando instala o git, instala o Docker com o próprio script de instalação do Docker, clona discourse_docker para /var/discourse e inicia o assistente de configuração.
wget -qO- https://raw.githubusercontent.com/discourse/discourse_docker/main/install-discourse | sudo bashSe o Docker já estiver instalado no servidor e preferir ver cada passo, faça o mesmo trabalho manualmente.
sudo -s
git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse
./discourse-setupExecute-o como root. Se for iniciado por um utilizador normal, discourse-setup termina imediatamente com This script must be run as root. Please sudo or log in as root first.. Sem o Docker instalado no servidor, termina com Docker is not installed. Please install Docker first., porque a clonagem manual não instala nada por si.
O que o assistente de configuração pergunta e o que grava
Em agosto de 2026, discourse-setup é apenas um wrapper fino. Executa discourse/setup-wizard:release como um contentor, com a rede do host e o socket do Docker montados, para que o assistente possa inspecionar a máquina que está a configurar. O assistente pede o hostname e os endereços de email dos administradores. Em seguida, pede a configuração SMTP. Grava containers/app.yml e depois faz o rebuild.
Antes de começar, é importante conhecer dois comportamentos. Se a máquina tiver pouca memória e não tiver swap, o assistente para e oferece-se para criar uma. O wrapper cria então um /swapfile de 2 GB, adiciona-o a /etc/fstab, define vm.swappiness = 10 em /etc/sysctl.d/30-discourse-swap.conf e inicia novamente o assistente. Quando o assistente termina, apresenta Rebuilding app in 5 seconds (Ctrl+C to cancel)... e executa ./launcher rebuild app no host. Esse build demora vários minutos num VPS pequeno. O primeiro é o mais lento, porque todos os assets são compilados de raiz.
./discourse-setup --help lista as flags relevantes quando algo corre mal. --skip-rebuild grava a configuração sem fazer o build, e --skip-connection-test ignora as verificações de DNS e das portas. Use --skip-connection-test apenas quando já souber por que motivo o teste falha, por exemplo, quando o host está atrás de uma firewall de rede que controla.
Leia o app.yml antes da primeira recompilação
O assistente grava um ficheiro que passa a ser da sua responsabilidade manter. Abra-o com sudo nano /var/discourse/containers/app.yml. Estas são as partes que determinam quase tudo.
templates:
- "templates/postgres.template.yml"
- "templates/redis.template.yml"
- "templates/web.template.yml"
- "templates/web.ratelimited.template.yml"
## Uncomment these two lines if you wish to add Lets Encrypt (https)
#- "templates/web.ssl.template.yml"
#- "templates/web.letsencrypt.ssl.template.yml"
expose:
- "80:80" # http
- "443:443" # https
env:
DISCOURSE_HOSTNAME: "forum.example.com"
DISCOURSE_DEVELOPER_EMAILS: "you@example.com"
DISCOURSE_SMTP_ADDRESS: smtp.example.com
DISCOURSE_SMTP_PORT: 587
DISCOURSE_SMTP_USER_NAME: user@example.com
DISCOURSE_SMTP_PASSWORD: "your-smtp-password"DISCOURSE_HOSTNAME é o endereço em que o site responde, e o Discourse cria os links a partir dele. Um valor incorreto faz com que o site carregue uma vez e depois redirecione para outro local. DISCOURSE_DEVELOPER_EMAILS é uma lista separada por vírgulas. Esses endereços tornam-se administradores automaticamente no primeiro registo. Coloque aí o seu próprio endereço e registe-se com ele. É assim que a primeira conta de administrador é criada.
O ficheiro armazena a sua palavra-passe SMTP em texto simples. Por isso, restrinja o diretório com sudo chmod 700 /var/discourse/containers. O formato também é YAML, o que significa que os espaços em branco fazem parte da configuração. Uma chave desalinhada faz a compilação falhar com um erro de análise e deixa o site indisponível. Existe uma armadilha documentada no próprio ficheiro de exemplo. Um # dentro de uma palavra-passe não delimitada inicia um comentário. Por isso, coloque entre aspas qualquer palavra-passe que contenha esse caractere.
O email é a etapa que impede a maioria das instalações
Em agosto de 2026, o assistente permite ignorar o SMTP e usar logins do Discourse ID. A opção app.yml inclui uma opção DISCOURSE_SKIP_EMAIL_SETUP correspondente, descrita nessa secção como a validação da configuração de email. Ignorar esta etapa é razoável para uma primeira avaliação do software. É uma má escolha para uma comunidade, porque, sem email de saída, ninguém consegue ativar uma conta ou redefinir uma palavra-passe.
O problema prático é que a maioria dos fornecedores de VPS bloqueia a porta de saída 25. Por isso, um servidor de email simples no próprio servidor não entrega mensagens. Use um relay autenticado na porta 587 ou na porta 465 com TLS implícito (segurança da camada de transporte). Para a porta 465, defina DISCOURSE_SMTP_FORCE_TLS: true. A configuração de exemplo recomenda esta opção para essa porta. Teste a conectividade a partir do host antes de recriar a instalação.
nc -vz smtp.example.com 587Um resultado correto é uma única linha terminada em succeeded!. Se o comando bloquear e terminar com timeout, a porta está bloqueada no caminho de saída da sua VPS. Nenhuma definição do Discourse corrige esse problema. Use uma porta permitida pelo fornecedor ou peça-lhe para abrir a porta.
Quando o site estiver disponível, envie uma mensagem de teste a partir da página Email em Admin. Em seguida, consulte os separadores Skipped e Bounced nessa mesma página. É nesses separadores que o Discourse regista as mensagens que recusou enviar e as mensagens que o relay rejeitou. Eles também indicam o motivo, o que é mais rápido do que consultar os logs.
TLS: deixar o contentor obter o seu próprio certificado
Se o Discourse for o proprietário das portas 80 e 443, use o mecanismo integrado de emissão. Retire os comentários das duas linhas do modelo SSL mostradas acima e, em seguida, faça uma nova compilação. O modelo controla o acme.sh, armazena os certificados no volume partilhado em /shared/ssl, renova-os segundo um agendamento dentro do contentor e configura o Discourse para forçar HTTPS.
A porta 80 tem de continuar acessível a partir da Internet para isto funcionar, porque o desafio HTTP é respondido nessa porta. Uma firewall que permita apenas a porta 443 produz uma compilação concluída, mas o certificado nunca é emitido. Verifique o resultado com ./launcher logs app imediatamente depois da nova compilação.
É melhor colocar nginx ou Caddy na frente?
Se o Discourse for o único serviço web no VPS, não faça isso. O contentor já executa um nginx ajustado, e um segundo proxy acrescenta um salto, outro certificado para renovar e uma nova fonte de problemas nos cabeçalhos.
Coloque um proxy na frente quando o mesmo VPS servir outros sites. Adicione templates/web.socketed.template.yml à lista de templates, comente as duas linhas expose e mantenha os dois templates SSL comentados. O contentor passa então a escutar num socket Unix em /var/discourse/shared/standalone/nginx.http.sock e deixa de manter portas abertas, libertando as portas 80 e 443 para o seu próprio proxy.
server {
listen 443 ssl;
server_name forum.example.com;
location / {
proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
}
}Os dois-pontos finais depois de .sock fazem parte da sintaxe de socket Unix do nginx, e sudo nginx -t recusa a configuração sem eles. X-Forwarded-Proto também é obrigatório. O Discourse escreve links absolutos. Sem esse cabeçalho, gera links http:// numa página HTTPS, e os browsers bloqueiam-nos como conteúdo misto. Com o contentor a usar um socket, o TLS passa a ser responsabilidade sua. Emita o certificado no host com Certbot no Ubuntu 24.04 e nginx. Se ainda não escolheu um proxy, a comparação entre nginx, Caddy e Traefik apresenta os compromissos envolvidos.
Rebuilds, upgrades e os comandos que vai realmente utilizar
cd /var/discourse
./launcher rebuild apprebuild destrói o contentor em execução, cria um novo a partir de app.yml e inicia-o. O site fica offline durante toda a compilação. Trate cada alteração de configuração como uma indisponibilidade programada de alguns minutos.
Alterar apenas valores em env: não exige isso. ./launcher destroy app && ./launcher start app recria o contentor a partir da imagem que já compilou, o que demora alguns segundos. Qualquer alteração em templates: ou hooks: modifica a própria imagem. Por isso, exige a compilação completa.
As atualizações chegam de duas formas. As versões intermédias são aplicadas a partir da interface Web em /admin/upgrade, através do plugin docker_manager que app.yml clona durante a compilação. As alterações à imagem base ou aos templates vêm do git.
cd /var/discourse
git pull
./launcher rebuild appAs compilações são onde os servidores pequenos falham, porque a compilação de assets é o pico de consumo de memória de todo o sistema. Se uma compilação parar a meio e dmesg mostrar uma linha como Out of memory: Killed process que identifique um processo ruby, faltou memória durante a compilação, mesmo que o site estivesse a funcionar corretamente antes. Adicione swap e execute novamente a compilação.
./launcher logs app
./launcher enter app
./launcher cleanuplogs mostra a saída do contentor, enter abre uma shell dentro dele e cleanup remove contentores que estejam parados há mais de 24 horas. Execute cleanup periodicamente, porque cada compilação deixa um contentor antigo e o disco de um VPS pequeno fica cheio sem chamar a atenção.
Backups e o ficheiro que o backup não contém
Faça backups a partir da página Backups em Admin. O arquivo fica no host em /var/discourse/shared/standalone/backups/default/. O mesmo trabalho pode ser executado numa shell.
cd /var/discourse
./launcher enter app
discourse backupdiscourse restore <filename> reverte o processo, e as restaurações são recusadas até executar discourse enable_restore. Essa proteção existe para impedir que um comando acidental substitua um fórum em produção.
Há duas lacunas que tem de corrigir manualmente. O arquivo contém a base de dados e contém os ficheiros enviados apenas quando está ativada a definição de backup que inclui uploads. Verifique essa definição antes de confiar no backup. O arquivo nunca contém app.yml. Por isso, uma restauração num VPS novo ainda precisa do seu hostname e do bloco SMTP. Também tem de copiar esse ficheiro para fora do servidor.
O arquivo também fica no mesmo disco que o site que protege. Isso não é um backup. Transfira-o para outro local segundo uma agenda.
rsync -avz root@forum.example.com:/var/discourse/shared/standalone/backups/default/ ~/discourse-backups/Quanto custa um fórum ativo em RAM
O bootstrap define UNICORN_WORKERS e db_shared_buffers com base na memória e na CPU detetadas, e a configuração de exemplo limita os buffers partilhados a um quarto da memória total. Cada worker do unicorn é um processo Ruby completo, e o Sidekiq executa tarefas em segundo plano ao lado deles. Por isso, o consumo de memória acompanha os pedidos concorrentes, não o número de membros registados. Um fórum pouco ativo, com algumas centenas de membros, não representa uma carga elevada. Normalmente, o que mais importa é o que partilha o servidor. Se for uma biblioteca de fotografias, os valores mínimos de RAM medidos em a comparação entre PhotoPrism e Immich mostram se uma reconstrução do Discourse ainda tem memória suficiente para terminar.
Não dimensione o servidor com base num número apresentado num artigo, incluindo este. Faça medições no seu próprio ambiente.
free -m
docker stats --no-streamO uso constante de swap juntamente com páginas lentas significa que falta RAM. Memória estável com páginas lentas normalmente indica outra causa. Por isso, leia ./launcher logs app antes de contratar um plano maior. Adicione também uma verificação externa ao servidor. Um fórum que fica sem memória às 3am falha silenciosamente. Um monitor de estado Uptime Kuma autoalojado num host separado avisa-o antes dos seus membros.
Quando o Discourse não é a escolha certa
O Discourse é uma aplicação grande, com uma instalação pesada e um ciclo de rebuild para cada configuração armazenada em app.yml. Esse custo oferece ferramentas de moderação completas e uma pesquisa que continua a funcionar quando o arquivo é grande. Para trinta pessoas que apenas querem um lugar para conversar, é mais máquina do que a conversa precisa. Leia primeiro a comparação de software de fóruns self-hosted e escolha o Discourse porque precisa das funcionalidades que ele oferece, não apenas porque já conhecia o nome.
FAQ
Posso instalar o Discourse num VPS sem um nome de domínio?
Não. A configuração distribuída indica que o Discourse não funciona com um endereço IP simples, e DISCOURSE_HOSTNAME é obrigatório. O Discourse cria ligações absolutas a partir desse nome de host, por isso um endereço IP nesse campo quebra as ligações e impede a emissão do certificado. Crie um registo A antes de começar e confirme com dig +short forum.example.com se ele resolve para o endereço do seu servidor.
Tenho de configurar o SMTP para concluir a instalação?
A partir de agosto de 2026, pode ignorá-lo. O assistente de configuração disponibiliza em alternativa os logins do Discourse ID, e app.yml inclui uma opção que ignora a validação da configuração de email. Para qualquer utilização além de um primeiro teste, configure-o, porque a ativação de contas e as reposições de palavras-passe são enviadas por email. Use um relay autenticado na porta 587 ou 465, porque a maioria dos fornecedores de VPS bloqueia o tráfego de saída na porta 25.
Porque falhou a reconstrução do meu Discourse a meio?
A memória é a causa mais comum. A compilação dos recursos durante a compilação precisa de mais memória do que o site em execução, por isso um servidor que disponibiliza o fórum sem problemas pode ainda assim falhar ao reconstruí-lo. Se dmesg mostrar Out of memory: Killed process a identificar um processo ruby, adicione swap (o swapfile criado pelo próprio assistente tem 2 GB) e execute ./launcher rebuild app novamente. Uma compilação que para devido a um erro YAML aponta, em vez disso, para um erro de indentação em app.yml.
O Discourse deve ficar atrás do meu próprio nginx ou Caddy?
Apenas quando o VPS também aloja outros sites. Se estiver sozinho no servidor, deixe o contentor manter as portas 80 e 443 e emitir o seu próprio certificado. Assim, há menos componentes para gerir. Para partilhar a máquina, adicione templates/web.socketed.template.yml, comente as linhas expose e faça proxy para o socket Unix em /var/discourse/shared/standalone/nginx.http.sock. Encaminhe X-Forwarded-Proto, caso contrário o Discourse gera ligações http:// numa página HTTPS.
Como faço uma cópia de segurança de um Discourse autoalojado?
Use a página Backups em Admin ou execute discourse backup depois de ./launcher enter app. Os arquivos ficam no host em /var/discourse/shared/standalone/backups/default/. Confirme que a opção que inclui os uploads está ativada, copie /var/discourse/containers/app.yml juntamente com o arquivo e transfira ambos para outra máquina, porque uma cópia de segurança no mesmo disco do site não sobrevive à falha que deveria permitir resolver.