Nibleaf: domínio próprio com HTTPS no Docker
Olá meus Unicórnios! 🦄✨
Instalar a documentação foi a parte fácil. O incômodo veio depois, quando
precisei mandar o endereço para alguém: http://203.0.113.10:4310.
😬 Ninguém digita isso, ninguém confia nisso, e o navegador ainda avisa que a
conexão não é segura. Documentação existe para ser lida, e um endereço feio
com IP e porta é um jeito muito eficiente de fazer as pessoas não lerem.
O que eu queria era o óbvio: docs.seusite.com.br, com cadeado.
Este artigo é esse caminho, do campo no painel até o certificado. E ele tem uma
curva no meio que eu não esperava, porque a tela do Nibleaf promete uma coisa
que a instalação no seu servidor não faz. Prefiro te contar isso agora do que
te deixar esperando um cadeado que nunca chega. 🙈
Se você ainda não tem o Nibleaf de pé, comece pelo artigo anterior: aqui eu parto de quem já tem o painel funcionando.
Nibleaf: documentação self-hosted com DockerA instalação completa com Docker Compose, as variáveis obrigatórias e os dois erros que travam todo mundo.blog.palomamacetko.com.br🏷️ Duas camadas que parecem a mesma coisa
Antes de mexer em qualquer campo, vale separar duas coisas que o painel mostra em telas vizinhas e que fazem papéis bem diferentes. Confundir as duas é o motivo de muita gente achar que configurou o domínio e não vê nada mudar.
| Camada | O que é |
|---|---|
| Deployment name | Um subdomínio grátis em .nibleaf.com, que já vem preenchido. É o endereço de fábrica do seu site publicado. |
| Custom domain | O seu nome, do seu registrador. É o que você quer, e ele sobrepõe o de cima. |
O primeiro mora em Settings → General, lá embaixo:
Repare no texto embaixo do campo: "Your free Nibleaf subdomain. Use
1-63 lowercase letters, numbers, and hyphens. Add a custom domain below to
override it." Ou seja, o próprio painel já avisa que o domínio próprio
ganha da configuração de cima. Você não precisa apagar o
Deployment name para usar o seu domínio: ele simplesmente deixa
de ser o endereço principal.
🌐 Adicionando o seu domínio
Vá em Settings → Custom domain. A tela nasce assim, com um campo e um botão:
Escreva o subdomínio que você quer usar. Eu recomendo um subdomínio
(docs.seusite.com.br) em vez do domínio raiz
(seusite.com.br), por um motivo bem prático que já já vai fazer
sentido: subdomínio aceita CNAME, e domínio raiz não aceita.
Clique em Add domain. A tela muda, o domínio aparece com a etiqueta Provisioning e surge a tabela que interessa:
📇 O que são esses dois registros, em português
Se você nunca mexeu com DNS, essa tabela parece um enigma. Ela não é: são duas frases, escritas num formato específico.
O DNS é a agenda telefônica da internet. Quando alguém digita
docs.seusite.com.br, o navegador não sabe onde isso fica; ele
pergunta ao DNS, e o DNS responde com o endereço da máquina. Criar um registro
é escrever uma linha nessa agenda.
| Registro | O que ele diz |
|---|---|
CNAME | "Quem procurar por docs.seusite.com.br, vá parar naquele servidor ali." É o registro que faz o endereço funcionar de verdade. |
TXT | "Eu sou mesmo o dono deste domínio, e aqui está a prova." Não leva ninguém a lugar nenhum: serve só para o Nibleaf conferir. |
O TXT merece um parágrafo, porque a razão de ele existir é
bonita. Qualquer pessoa pode entrar no painel dela e digitar o seu
domínio no campo Custom domain. O que impede essa pessoa de sequestrar
o seu endereço é justamente essa prova: só quem tem a senha do painel de DNS do
domínio consegue criar um registro dentro dele. O Nibleaf sorteia um código,
manda você publicá-lo no DNS, e depois confere se está lá. Quem conseguiu
publicar, é dono. 🔐
Repare também no nome do registro TXT:
_nibleaf.docs.seusite.com.br. Aquele ponto no começo, e o
_nibleaf antes do seu subdomínio, não são enfeite nem erro de
digitação. É um nome separado, num cantinho reservado, para a prova não se
misturar com o endereço que serve o site. Copie exatamente como está.
🖱️ Onde ficam esses campos no seu provedor
Cada empresa dá um nome diferente para a mesma tela. Procure no painel do lugar onde você registrou o domínio (ou onde ele está apontado hoje) por algo assim:
| Nome que você vai encontrar | Onde costuma estar |
|---|---|
| Zona de DNS, DNS Zone, Gerenciar DNS | Registro.br, GoDaddy, Hostgator, Locaweb |
| DNS Records, DNS Management | Cloudflare, Namecheap |
| Editar zona, Advanced DNS | painéis mais antigos |
Lá dentro haverá um botão de Adicionar registro, e um formulário com
três campos: o tipo (uma listinha onde você escolhe
CNAME ou TXT), o nome e o
valor. É copiar da tabela do Nibleaf e colar, uma linha de cada
vez, duas vezes no total.
Dois detalhes que confundem quase todo mundo na primeira vez:
- Alguns painéis querem só a primeira parte do nome. Se o
campo já mostra
.seusite.com.brescrito ao lado, escreva apenasdocs, e nãodocs.seusite.com.br. Escrever o nome inteiro nesse caso criadocs.seusite.com.br.seusite.com.br, que não é o que você quer. - O campo TTL pode ficar como está. Ele diz por quantos segundos os outros servidores podem guardar essa resposta na memória. Se o painel deixar escolher e você estiver testando, ponha o menor valor disponível (costuma ser 300, ou cinco minutos): erro cometido com TTL alto demora muito mais para ser corrigido.
⏳ Por que a propagação demora
Salvou os dois registros e nada acontece? É esperado. A tela do Nibleaf até avisa, discretamente: "DNS can take a few minutes to propagate."
O motivo é aquele TTL do parágrafo acima. A agenda da internet
não é um lugar só: é uma porção de servidores espalhados que guardam cópias das
respostas para não perguntar tudo de novo o tempo todo. Quando você cria o
registro, os que nunca perguntaram pelo seu domínio pegam a resposta nova na
hora; os que já perguntaram antes continuam com a resposta velha até o prazo
vencer. Por isso o site pode abrir no seu celular e não abrir no seu
computador. Não é defeito, é cache. 🕰️
Você pode perguntar direto, sem esperar, com o comando abaixo. O
+short pede uma resposta curta, sem o relatório inteiro:
dig +short docs.seusite.com.br
dig +short TXT _nibleaf.docs.seusite.com.br
Se o comando dig não existir na sua máquina, instale com
apt install dnsutils no Ubuntu. Quando as duas linhas responderem
o que você cadastrou, volte ao painel e clique em Verify DNS.
O status sai de Provisioning.
🎯 O cabeçalho Host, que é o coração de tudo isto
Aqui está o mecanismo que faz o recurso funcionar, e entender isso vale mais que qualquer campo de formulário. 🙏
Um servidor pode atender muitos sites ao mesmo tempo, no mesmo endereço e na
mesma porta. Como ele sabe qual entregar? Pelo cabeçalho
Host: junto com o pedido, o navegador manda uma linha
dizendo qual nome ele digitou. O Nibleaf lê essa linha e decide: se o nome for
o do painel, entrega o painel; se for um domínio cadastrado, entrega o site de
documentação daquele projeto.
Dá para ver esse mecanismo com os próprios olhos, sem ter domínio nenhum,
mandando o cabeçalho na mão. O -H do curl força o
cabeçalho, e o -w mostra só o código da resposta:
# Com o endereco que o Nibleaf conhece:
curl -s -o /dev/null -w '%{http_code}\n' \
-H 'Host: SEU_IP_AQUI:4310' http://127.0.0.1:4310/sign-in
# Com um nome que ele nao conhece:
curl -s -o /dev/null -w '%{http_code}\n' \
-H 'Host: docs.exemplo.com.br' http://127.0.0.1:4310/sign-in
O resultado, no mesmo servidor, no mesmo caminho, no mesmo segundo:
200
404
Isso mesmo, o mesmo pedido! 🤯 Só mudou o nome que o
navegador diz estar procurando. É por isso que o passo do DNS não basta
sozinho: de nada adianta o mundo inteiro saber que
docs.seusite.com.br mora no seu servidor, se o pedido chegar lá
dentro com outro nome escrito nele.
E é exatamente aqui que mora o erro mais comum ao pôr um proxy na frente.
Um proxy reverso, por padrão, reescreve esse cabeçalho com o endereço do
destino, porque é o que faz sentido na maioria dos casos. Com o Nibleaf isso
quebra tudo: o pedido chega com o nome do contêiner, que ele não reconhece,
cai no roteamento de sites publicados, e sai 404. No artigo da
instalação isso apareceu como um problema a consertar; aqui é o contrário, é a
peça que faz o domínio funcionar.
🔐 Quem emite o certificado (a parte que a tela não conta)
Agora a curva que eu prometi na introdução. Olhe de novo a frase no alto da tela Custom domain: "Add your domain, follow the DNS steps, and Nibleaf will automatically provision and renew TLS."
Lendo isso, qualquer pessoa entende que é só cadastrar o domínio e o cadeado aparece sozinho. Foi o que eu entendi. 😅 Mas a documentação interna do projeto, que vai junto dentro da imagem do Docker, tem uma seção honesta chamada "Partial / Deliberate Limits". Você pode ler direto do contêiner:
docker exec nibleaf-app-1 grep -A3 'TLS certificates' /app/IMPLEMENTATION_STATUS.md
E lá está, com todas as letras:
- TLS certificates and wildcard/custom-domain ingress are handled by Coolify or
the operator's reverse proxy. The app resolves hosts and serves content once
traffic reaches it.
Traduzindo: o certificado é responsabilidade do Coolify (uma plataforma de hospedagem que algumas pessoas usam por fora) ou do proxy reverso do operador. E o operador é você. 🫠 O Nibleaf faz a parte dele com competência (reconhece o domínio e serve o conteúdo certo), mas quem obtém e renova o HTTPS está do lado de fora.
A boa notícia é que a parte que sobrou para você é pequena, e a ferramenta que resolve isso é a mesma que já usamos no artigo anterior para esconder as páginas de propaganda: o Caddy. Ele obtém e renova certificado do Let's Encrypt sozinho, de graça, sem você configurar nada, com uma condição: que você lhe dê um nome de domínio no lugar de uma porta.
🚪 O proxy que resolve o cadeado
No artigo da instalação o arquivo de regras começava com :4311,
que quer dizer "escute na porta 4311, sem se preocupar com nome". Trocar essa
primeira linha por um domínio muda tudo: o Caddy passa a escutar nas portas 80
e 443, pede o certificado para aquele nome e renova sozinho quando estiver
perto de vencer. 🎉
Abra o arquivo de regras. Se você seguiu o artigo anterior, ele está aqui:
nano /opt/nibleaf-porta/Caddyfile
Para colar no terminal, lembre que o Ctrl+V de sempre
não funciona: use Ctrl+Shift+V ou
o botão direito do mouse. Deixe o arquivo assim, trocando
docs.seusite.com.br pelo seu domínio e
SEU_IP_AQUI pelo endereço do seu servidor:
docs.seusite.com.br {
# Todo o trafego vai para o Nibleaf.
reverse_proxy 172.17.0.1:4310 {
# Sem esta linha o Nibleaf devolve 404. Ele decide o que
# servir pelo nome que o navegador pediu, e o proxy trocaria
# esse nome pelo endereco do conteiner.
header_up Host docs.seusite.com.br
}
}
# O painel de administracao continua onde estava.
:4311 {
redir / /sign-in 302
reverse_proxy 172.17.0.1:4310 {
header_up Host SEU_IP_AQUI:4310
}
}
Salve com Ctrl+O, confirme com
Enter e saia com
Ctrl+X. Aquele ^ na barra de baixo do
editor significa a tecla Ctrl: onde se lê ^O,
entenda Ctrl+O.
Repare nos dois blocos. O de cima é o site público, que ganha cadeado; o de
baixo é o painel onde você escreve, que continua na porta 4311 como antes. São
públicos diferentes: o mundo lê a documentação, só você administra. E repare
que o header_up Host aparece nos dois, com valores diferentes,
cada um dizendo ao Nibleaf qual das duas coisas entregar.
✅ Conferindo o arquivo antes de subir
Erro de digitação em arquivo de configuração é chato porque o programa às vezes sobe assim mesmo e falha só na hora errada. O Caddy tem um comando que lê o arquivo e diz se ele faz sentido, sem subir nada:
docker run --rm \
-v /opt/nibleaf-porta/Caddyfile:/etc/caddy/Caddyfile:ro \
caddy:2-alpine caddy validate --config /etc/caddy/Caddyfile
O que interessa são a última linha e uma no meio:
{"level":"info","logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
Valid configuration
O Valid configuration do fim diz que a sintaxe está boa. Mas a
linha do meio é a que dá gosto de ver: enabling automatic HTTP->HTTPS
redirects. O Caddy leu o nome de domínio no topo do arquivo, entendeu
sozinho que ali vai haver HTTPS, e já se programou para mandar quem chegar por
http:// para o https://. Você não pediu isso em lugar
nenhum. 💚
🔥 Abrindo as portas certas (e fechando as erradas)
Certificado do Let's Encrypt não se compra: se prova. O serviço vai bater no seu domínio para confirmar que ele é seu, e essa visita chega pelas portas padrão da web. Se elas estiverem fechadas, a emissão falha sem drama nenhum, só não acontece.
ufw allow 80/tcp
ufw allow 443/tcp
A 80 é a do http:// e a 443 é a do https://. As
duas precisam estar abertas: a 80 porque é por ela que a validação costuma
chegar, e a 443 porque é por ela que os seus leitores vão entrar depois.
E agora feche o que não deve mais ficar aberto. Com o proxy na frente, ninguém precisa alcançar a porta da aplicação diretamente:
ufw delete allow 4310/tcp
ufw status
🚀 Subindo o proxy com o novo arquivo
O contêiner precisa publicar as portas 80 e 443 agora, e as portas de um contêiner só se definem na criação. Então o caminho é remover e criar de novo. Não se assuste: o arquivo de regras mora fora do contêiner, então nada de configuração se perde.
docker rm -f nibleaf-porta
O rm remove e o -f manda parar antes de remover,
sem perguntar. Agora suba o novo:
docker run -d --name nibleaf-porta --restart unless-stopped \
-p 80:80 -p 443:443 -p 4311:4311 \
-v /opt/nibleaf-porta/Caddyfile:/etc/caddy/Caddyfile:ro \
-v nibleaf-porta-dados:/data \
caddy:2-alpine
Duas novidades em relação ao comando do artigo anterior, e as duas importam:
-p 80:80 -p 443:443publica as portas da web, que é por onde o certificado é emitido e por onde os leitores chegam.-v nibleaf-porta-dados:/dataé o detalhe que muita gente esquece. É ali que o Caddy guarda os certificados que já obteve. Sem esse volume, cada vez que o contêiner for recriado ele pede tudo de novo, e o Let's Encrypt tem limite de pedidos por semana por domínio. Estoure o limite e você fica sem cadeado por dias, esperando o prazo virar. 😱
Acompanhe o que está acontecendo. O -f deixa o registro rolando
ao vivo, e você sai com Ctrl+C:
docker logs -f nibleaf-porta
A emissão leva alguns segundos. Quando terminar bem, aparece uma linha com
certificate obtained successfully citando o seu domínio. Se
aparecer erro falando em challenge ou timeout, quase sempre é
uma destas três coisas: o DNS ainda não propagou, a porta 80 está fechada, ou o
CNAME aponta para o lugar errado. Vale conferir na ordem.
🔎 Testando de fora
O teste que vale é do seu computador, não de dentro do servidor. O
-I pede só os cabeçalhos, sem baixar a página inteira:
curl -I https://docs.seusite.com.br
Um HTTP/2 200 na primeira linha significa que deu certo: o
cadeado está de pé (o curl recusaria um certificado inválido sem
você mandar), e o Nibleaf reconheceu o domínio e serviu a documentação.
Confira também o redirecionamento automático, que o Caddy montou sozinho.
O -s cala o relatório de progresso e o -o /dev/null
joga fora o corpo da resposta, deixando só o que interessa:
curl -s -o /dev/null -w '%{http_code} -> %{redirect_url}\n' \
http://docs.seusite.com.br
A resposta esperada é um 308 apontando para o endereço com
https://. Ou seja: quem digitar o endereço sem o s
vai parar na versão segura de qualquer jeito, sem precisar saber de nada
disso. 🌈
E, agora sim, abra https://docs.seusite.com.br no navegador.
Cadeado na barra, nome bonito, sem porta pendurada no fim. É este endereço que
você manda para as pessoas. 💜
Por hoje é só, meus unicórnios! 🦄✨
Que a magia do arco-íris continue brilhando em suas vidas! Até mais! 🌈🌟
Perguntas frequentes
Quem emite o certificado HTTPS numa instalação self-hosted?
Por que o meu domínio devolve 404 mesmo já cadastrado?
Host da requisição. Se o proxy repassa o pedido trocando esse cabeçalho pelo endereço do contêiner, o Nibleaf não reconhece o nome e a requisição cai no roteamento de sites publicados, onde não existe nada. A correção é o header_up Host mandando o nome do domínio.Para que serve o registro TXT em _nibleaf?
docs.seusite.com.br no painel dela; só quem tem acesso ao painel de DNS do domínio consegue criar o TXT com o valor nibleaf-verify= que o Nibleaf sorteou. Sem essa prova, o domínio fica em Provisioning para sempre.Quanto tempo o DNS demora para propagar?
TTL do registro. Se você puder escolher, ponha um TTL baixo (300 segundos) antes de mexer: a mudança seguinte propaga em cinco minutos em vez de um dia.Qual a diferença entre o subdomínio grátis e o domínio próprio?
Deployment name, em Settings → General, monta um endereço em .nibleaf.com e vem preenchido de fábrica. O Custom domain é o seu próprio nome, e a própria tela avisa que adicionar um domínio próprio sobrepõe o subdomínio grátis.Preciso abrir a porta 4310 no firewall para usar o domínio?
Leia também
OpenSEO: instalando na VPS e auditando um site
Como instalar o OpenSEO, alternativa aberta ao Semrush, numa VPS com Docker, auditar um site de verdade e entender a chave da DataForSEO.
changedetection.io: vigiando mudanças em sites na sua VPS
Instale o changedetection.io numa VPS com Docker, vigie um site com filtro CSS, receba o aviso no celular e acabe com o alarme falso do RSS.
Project NOMAD: seu servidor offline de emergência
Instale o Project NOMAD numa VPS Ubuntu e tenha Wikipédia, mapas, cursos e IA funcionando sem nenhuma conexão com a internet.