Pular para o conteúdo
Docker

Nibleaf: domínio próprio com HTTPS no Docker

Um unicórnio de crina luminosa segurando um cadeado dourado que projeta um escudo sobre um castelo de documentação, enquanto uma coruja voa carregando dois pergaminhos de registro

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.

CamadaO que é
Deployment nameUm subdomínio grátis em .nibleaf.com, que já vem preenchido. É o endereço de fábrica do seu site publicado.
Custom domainO seu nome, do seu registrador. É o que você quer, e ele sobrepõe o de cima.

O primeiro mora em Settings → General, lá embaixo:

A tela General do Nibleaf com o campo Deployment name preenchido com docs e o sufixo ponto nibleaf ponto com ao lado

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:

A tela Custom domain do Nibleaf vazia, com o campo de digitar o domínio e o botão Add domain

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:

A tela Custom domain com o domínio adicionado, mostrando o status Provisioning e a tabela com os registros CNAME e TXT a criar no provedor

📇 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.

RegistroO 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 encontrarOnde costuma estar
Zona de DNS, DNS Zone, Gerenciar DNSRegistro.br, GoDaddy, Hostgator, Locaweb
DNS Records, DNS ManagementCloudflare, Namecheap
Editar zona, Advanced DNSpainé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.br escrito ao lado, escreva apenas docs, e não docs.seusite.com.br. Escrever o nome inteiro nesse caso cria docs.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:443 publica 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?
Você, pelo seu proxy reverso. A tela diz que o Nibleaf provisiona e renova o TLS sozinho, mas a documentação interna do projeto lista isso entre os limites deliberados: o certificado é responsabilidade do Coolify ou do proxy do operador. O Nibleaf só resolve o domínio e serve o conteúdo depois que o tráfego chega até ele.
Por que o meu domínio devolve 404 mesmo já cadastrado?
O Nibleaf decide o que servir pelo cabeçalho 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?
Ele prova que o domínio é seu. Qualquer pessoa pode digitar 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?
De alguns minutos a algumas horas, e a espera é normal. Cada servidor de DNS no caminho guarda a resposta antiga pelo tempo do 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?
O 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?
Não, e é melhor fechá-la. Quem passa a receber a visita é o proxy, nas portas 80 e 443. O Nibleaf continua escutando na 4310 só para quem já está dentro do servidor, e o proxy conversa com ele por ali. Porta de aplicação aberta para a internet é propaganda acessível por fora e HTTP sem cadeado.

Leia também