Hospedado em casa: como este site chega à internet
“Hospedado em casa”, no rodapé, descreve onde o trabalho acontece: as aplicações deste site e seus bancos de dados executam em um Acer Aspire reaproveitado como servidor, conectado à rede e à energia de uma residência. Uma requisição pode começar em um celular do outro lado do mundo e terminar nesse computador. O navegador não se conecta diretamente ao endereço residencial: a Cloudflare recebe o tráfego público e o entrega por um túnel iniciado pelo Aspire.
Isso não significa que toda a infraestrutura está dentro de casa. O domínio depende do Registro.br e do DNS; a entrada HTTPS depende da Cloudflare; o código e as imagens passam pelo GitHub; os backups saem da máquina para o Cloudflare R2. O que fica em casa é a execução das aplicações e o armazenamento primário do banco. A conexão residencial, a energia, os discos e o computador continuam sendo dependências reais do site.
Este texto reúne a antiga documentação de sistema, rede e DNS. Mantém a publicação original de 11 de agosto de 2026, mas descreve a configuração verificada em 1º de outubro de 2026. Foi gerado por IA a partir do repositório e de inspeções do servidor. É uma referência para pessoas e agentes que precisam entender o ambiente; informações de acesso, credenciais e dados privados ficam fora desta página pública.
Uma requisição, do navegador ao Aspire
O DNS público leva o visitante à Cloudflare. Ela termina a conexão HTTPS e encaminha a requisição pelo Cloudflare Tunnel. O processo cloudflared, dentro de um container no Aspire, entrega a requisição a http://nginx:80. O nginx escolhe o serviço pelo caminho solicitado. A resposta percorre o caminho de volta até o navegador.
O túnel é uma conexão de saída estabelecida e mantida pelo servidor. “Saída” descreve quem iniciou a conexão; depois disso, ela transporta requisições e respostas nas duas direções. O hostname do túnel não é uma porta aberta no roteador, e não é preciso apontar o DNS para o IP residencial. Cloudflare Tunnel
O trecho público usa HTTPS. A conexão entre cloudflared e o nginx usa HTTP dentro das redes Docker; o nginx também conversa por HTTP com o frontend e as APIs. Esses serviços não publicam suas portas individualmente na internet. Esse desenho limita a exposição, mas continua exigindo autenticação e autorização nas aplicações: um proxy ou túnel não transforma uma rota administrativa em rota privada.
O computador que executa o site
O servidor é um Acer Aspire 5750, da geração de 2011. Ele deixou de funcionar como desktop de uso diário e passou a executar serviços sem sessão gráfica. Na inspeção usada para atualizar este texto, o ambiente era:
| Item | Configuração verificada |
|---|---|
| Processador | Intel Core i5-2410M, 2 núcleos e 4 threads, frequência nominal de 2,30 GHz |
| Memória | 10 GB instalados; o Linux apresenta aproximadamente 9,6 GiB utilizáveis |
| Sistema operacional | Linux Mint 22.3, Zena, baseado em Ubuntu 24.04 |
| Kernel | Linux 6.8.0-124-generic, arquitetura x86-64 |
| Inicialização | multi-user.target; gerenciador de login gráfico inativo |
| Disco do sistema | SSD Crucial de 240 GB nominais; nele está montado / |
| Armazenamento adicional | HDD Samsung de 640 GB e SSD Western Digital de 120 GB nominais |
| Conexão utilizada | Ethernet; Wi-Fi estava inativo na inspeção |
| Fuso horário | America/Sao_Paulo |
Capacidade nominal em GB e capacidade apresentada em GiB não são a mesma medida. Os discos adicionais existem no host, mas sua presença não significa que sejam réplicas do banco ou backups deste projeto. O dado persistente do site é um volume Docker específico; a cópia de segurança do banco é enviada para fora do servidor.
Há outros serviços nessa máquina. Este documento cobre somente o site e sua infraestrutura. Não se deve assumir que toda a RAM, CPU ou capacidade de disco do Aspire esteja disponível para uma nova aplicação. Também não se deve confundir memória usada como cache pelo Linux com memória irrecuperavelmente ocupada: o indicador available é mais útil para avaliar margem de operação do que free isoladamente.
Essa é uma máquina residencial, não um datacenter com redundância contratada. O site pode ficar indisponível por falha de energia, conexão, disco, host, aplicação ou provedor externo. Reaproveitar o hardware reduz o custo de hospedagem e torna as decisões de infraestrutura observáveis na prática; o custo é assumir a manutenção e essas dependências.
Endereços diferentes, alcances diferentes
Existem várias redes envolvidas. Um nome ou endereço só tem sentido dentro do contexto em que é usado.
| Contexto | Como alcançar o site ou serviço |
|---|---|
| Visitante na internet | https://jpsalviano.com.br, pela Cloudflare |
| Shell no próprio Aspire | http://127.0.0.1:18082, entrada local do nginx |
| Administrador autorizado | SSH pelo nome aspire, conforme a configuração privada de Tailscale e SSH |
| Container do frontend | http://blog-api:8000 ou http://bolao-api:8000, por DNS interno do Docker |
| API dentro do Compose | postgres:5432, com o usuário e banco exclusivos da aplicação |
| Navegador do visitante | URLs relativas como /api/v1/bolao/...; nomes internos não existem no DNS público |
O host tem um endereço privado na LAN, fornecido pela rede residencial. Ele pode mudar: o endereço publicado na documentação antiga não é uma referência de acesso confiável. Para administração, usa-se a identidade privada do servidor na tailnet; para o público, usa-se o domínio. Esta página não publica IPs privados de dispositivos, endereço WAN, identificadores de túnel ou informações sobre a localização da residência.
127.0.0.1 sempre significa a própria máquina ou namespace de rede de quem está usando o endereço. No navegador, seria o computador do visitante. Dentro de um container, seria aquele container. Já blog-api é um nome de serviço do Docker, resolvido apenas no contexto da rede correspondente. Copiar esses endereços sem distinguir o contexto é uma fonte comum de diagnóstico incorreto.
Por que foi escolhido um túnel: NAT e CGNAT
A configuração que motivou o projeto tinha NAT no roteador doméstico e CGNAT no provedor. O diagnóstico original registrou a WAN do roteador na faixa 100.64.0.0/10, reservada para espaço compartilhado de provedores. Esse registro é o contexto histórico da decisão, não uma nova leitura do painel WAN do roteador nesta atualização. RFC 6598
Em uma conexão IPv4 desse tipo, o endereço público visível externamente pode ser compartilhado entre assinantes. Configurar encaminhamento de portas no roteador residencial não cria uma regra no equipamento do provedor. DDNS atualiza um nome quando o IP muda, mas não oferece controle sobre esse NAT adicional. Por isso, nenhum dos dois resolve sozinho a entrada IPv4 não solicitada nesse cenário.
A escolha foi fazer o servidor abrir uma conexão de saída até um ponto publicamente alcançável. A Cloudflare recebe as conexões dos visitantes e usa o túnel já estabelecido para chegar ao nginx. Mudanças de IP residencial não exigem alterar o DNS do site, desde que o servidor continue conseguindo sair para a internet e restabelecer o túnel.
CGNAT não significa que toda forma de acesso de entrada seja impossível. IPv6 é uma possibilidade distinta, sujeita à disponibilidade, ao firewall e ao alcance dos clientes; um relay ou VPN também pode fornecer outro caminho. O projeto escolheu Cloudflare Tunnel por conveniência operacional e independência do endereço residencial. Não depende de visitantes conseguirem alcançar uma porta encaminhada no roteador.
Uma cautela importante: Tailscale também usa endereços da faixa 100.64.0.0/10. Ver esse prefixo em uma interface de Tailscale não comprova CGNAT do provedor; o diagnóstico precisa distinguir a interface privada da WAN residencial.
Domínio: registro, delegação e DNS
O endereço público é jpsalviano.com.br. Registro do domínio e resposta DNS são funções diferentes:
| Responsabilidade | Papel neste projeto |
|---|---|
| Registro do domínio | Registro.br, dentro da estrutura do domínio .br administrada pelo NIC.br |
| Titular | Responsável por manter o domínio registrado e suas configurações |
| DNS autoritativo | Cloudflare, após delegação dos nameservers no Registro.br |
| Proxy HTTPS e entrada pública | Cloudflare, integrada ao túnel |
| Origem da aplicação | Aspire, alcançado pelo cloudflared |
A delegação atual usa aryanna.ns.cloudflare.com e pranab.ns.cloudflare.com, confirmados por consulta DNS. O domínio continua registrado no Registro.br; trocar os nameservers muda quem responde pela zona, sem transferir o registro administrativo.
Uma resolução sem cache segue referências desde a raiz até .br, depois até os servidores autoritativos da Cloudflare. O resolver recursivo faz esse trabalho para o cliente e guarda as respostas conforme o TTL. Não é preciso percorrer essa cadeia em cada visita: caches do navegador, do sistema e do resolver podem responder antes.
O desenho de roteamento usa um CNAME para um hostname sob cfargotunnel.com, com proxy da Cloudflare. O identificador específico do túnel fica na configuração privada. Na consulta pública, o visitante recebe registros A e AAAA da edge da Cloudflare, não o IP doméstico. A existência de uma resposta AAAA para o site também não prova que o Aspire tenha uma origem IPv6 pública: a edge e o servidor residencial são camadas diferentes. O flattening permite resolver o CNAME até endereços utilizáveis pelo cliente. CNAME flattening
Glue records são endereços auxiliares usados em situações de delegação, especialmente quando o nome do servidor está dentro da zona delegada. Os nameservers deste domínio estão sob cloudflare.com; não há a circularidade de precisar alcançar um servidor chamado, por exemplo, ns1.jpsalviano.com.br para descobrir o próprio endereço. Terminologia DNS, RFC 9499
O painel privado é a fonte para o registro do túnel; a resposta DNS pública é a fonte para o que os visitantes resolvem. Consultar apenas A e AAAA não revela toda a configuração da zona. A continuidade do serviço depende de manter o domínio, a delegação, a rota pública e o túnel válidos.
Administração privada: Tailscale e SSH
A administração cotidiana usa Tailscale e SSH. Tailscale fornece uma rede privada entre dispositivos autorizados; SSH fornece a sessão no sistema operacional. A política de acesso da tailnet e a autenticação SSH determinam quem pode entrar. O domínio público do blog não é o endereço de administração.
No ambiente de trabalho configurado, ssh aspire chega ao servidor usando a configuração privada do cliente. Uma pessoa ou agente em outro computador não ganha acesso apenas por conhecer esse alias: precisa estar autorizado e possuir a configuração e autenticação apropriadas.
O fato de um daemon SSH escutar em uma interface do host não prova exposição à internet. Firewall, redes privadas, NAT e políticas de acesso fazem parte do alcance efetivo. Da mesma forma, conhecer o IP da LAN não substitui autorização. Esta documentação explica os caminhos; ela não distribui credenciais.
Containers, usuários e redes Docker
O projeto Compose de produção se chama aspire-web-rootless. O Docker das aplicações funciona em modo rootless, sob o usuário de serviço docker-services. O usuário de administração salviano não tem acesso direto a esse daemon. Um container rootless não é uma licença para ignorar permissões: ele tem os acessos concedidos ao usuário e aos recursos montados.
O runner do GitHub Actions no Aspire executa como docker-services. É ele que consegue atualizar os containers por meio do workflow. Ter uma sessão SSH como salviano não implica conseguir executar docker compose contra a produção. A separação é intencional.
Há duas redes definidas no Compose:
| Rede | Participantes | Função |
|---|---|---|
internal | Postgres, APIs, frontend e nginx | Comunicação entre serviços; configurada com internal: true, sem saída direta para a internet |
edge | nginx e cloudflared | Conexão do túnel com a internet e entrega ao proxy interno |
O nginx participa das duas redes e faz a ponte HTTP entre o ingresso e os serviços. O cloudflared participa da rede com saída. O banco, as APIs e o frontend ficam na rede interna. Se uma nova aplicação precisar chamar uma API externa, essa necessidade terá de ser atendida explicitamente na configuração de rede; não se deve assumir que todo container já tenha acesso de saída.
O único mapeamento de porta do site no host é 127.0.0.1:18082:80, para o nginx. A vinculação ao loopback permite verificações no próprio Aspire sem publicar essa porta em todas as interfaces. As portas 8000 das APIs, 3000 do frontend e 5432 do Postgres são internas aos serviços. Containers podem usar a mesma porta interna sem conflito porque têm namespaces de rede separados.
Aplicações independentes em um mesmo site
O site é uma entrada para aplicações com prefixos próprios. Hoje estão online:
| Caminho | Responsável |
|---|---|
/ | Página inicial com acesso ao blog e ao bolão |
/blog | Arquivo público do blog |
/blog/posts/<slug> | Conteúdo de um post |
/blog/admin | Administração autenticada do blog |
/bolao | Classificações públicas por campeonato e ranking geral de tipsters |
/bolao/palpites | Histórico, palpites e administração, conforme autenticação |
/api/v1/blog/... | Serviço blog-api |
/api/v1/bolao/... | Serviço bolao-api |
O antigo caminho /copa redireciona para /bolao. O nginx também encaminha o prefixo antigo da API de Copa durante a transição, para clientes e monitores existentes. Isso é compatibilidade de uma aplicação específica, não uma API compartilhada no domínio inteiro.
Na página principal do bolão, o campeonato ativo abre sua classificação automaticamente; os demais mostram o título e só renderizam a tabela quando o visitante o ativa. Abaixo deles, o ranking geral de tipsters permanece visível e ordena a soma de pontos dividida pela soma de palpites de todos os campeonatos. Só entram jogos iniciados com resultado confirmado; palpites de partidas pendentes ou futuras não aparecem nesses agregados. Médias iguais compartilham a posição. É uma média de pontos por palpite, diferente da porcentagem de aproveitamento exibida dentro de cada campeonato.
O nginx roteia /api/v1/blog/ e /api/v1/bolao/ para suas respectivas APIs. Demais caminhos de API sob /api/v1/ recebem 404 quando não pertencem a um prefixo conhecido. O restante vai ao Next.js, inclusive as rotas de interface e de ações do painel do blog.
O frontend usa Next.js 16, React e TypeScript. O layout raiz só prepara o documento, fontes e estilos; cada aplicação escolhe seu layout e pode usar o SiteShell, responsável pelo cabeçalho, coluna de leitura e rodapé. É nesse componente compartilhado que a expressão “hospedado em casa” ganha presença visual. A altura acompanha a área visível do navegador móvel, e posts longos têm navegação de retorno e um controle para voltar ao topo.
As APIs usam Python 3.13, FastAPI, Uvicorn, SQLAlchemy assíncrono e asyncpg. Cada aplicação tem pacote, imagem, dependências com lockfile, configuração, testes e cadeia de migrações próprios. O código compartilhado fica em app/core; as aplicações não importam umas às outras. FastAPI continua sendo a interface de dados, em vez de o Next.js acessar diretamente o banco.
O nginx consulta o DNS interno do Docker em tempo de requisição. Uma recriação de API pode mudar seu endereço interno; não é necessário recarregar o nginx depois de cada deploy para recuperar um IP novo. Essa independência também permite atender uma aplicação enquanto outra está indisponível.
Banco de dados e conteúdo publicado
Existe um servidor Postgres 17, com bancos distintos blog e bolao e usuários exclusivos. Não são apenas dois usuários dentro do mesmo banco. Cada aplicação recebe sua própria URL e seus próprios segredos. As permissões de conexão são restritas; as aplicações não devem consultar dados umas das outras.
O volume persistente se chama aspire-web-rootless-postgres e é externo ao Compose. Recriar uma imagem não deve recriar um banco vazio. O nome do projeto e o nome do volume têm significado operacional e não devem ser alterados casualmente: publicação, migrações e backups dependem dessa identidade.
No blog, os arquivos Markdown versionados em content/posts/ entram na imagem da API. O comando de sincronização lê o arquivo e atualiza o registro no banco. As requisições públicas leem o Postgres; não abrem o arquivo Markdown a cada visita. Arquivar um arquivo em Git, isoladamente, também não retira o post da publicação: o status no banco precisa ser atualizado.
A data published_at controla a posição no arquivo mensal. created_at registra a criação do registro do post; updated_at registra sua última atualização no banco. A página mostra os três conceitos separadamente, com horários de criação e atualização no fuso de São Paulo. Atualizar este texto mantém sua publicação em agosto e sua identidade original.
Tags podem ser declaradas no frontmatter e aparecem no arquivo e no detalhe. ai-generated identifica a origem do texto; a página também exibe “Texto gerado por IA”. Sincronizar um arquivo sem o campo tags preserva atribuições existentes; uma lista explícita substitui as tags, e tags: [] as remove. A sincronização não transforma automaticamente um post despublicado em publicado.
Autenticação e segredos
O blog e o bolão têm autenticação própria. O painel do blog usa cookie protegido e valida as ações em uma rota do Next.js; a API do blog valida um token administrativo. O bolão usa sessão assinada e credenciais próprias. Não há uma migração para Google OAuth em execução documentada no código atual.
Segredos são fornecidos por arquivos privados do servidor e não entram no Git, nos arquivos Markdown ou nas imagens como conteúdo versionado. Cada container recebe apenas os segredos necessários à sua aplicação. Credenciais de DNS, túnel, banco, backup e administração não devem aparecer em exemplos, logs ou diagnósticos publicados.
Respostas privadas do bolão não devem ser tratadas como dados públicos cacheáveis. O acesso pelo túnel mantém a aplicação disponível, mas as decisões sobre quem pode ver ou alterar um dado continuam sendo responsabilidade da aplicação.
Limites para compartilhar o hardware
A configuração de produção estabelece limites por serviço:
| Serviço | Limite de memória | Limite de CPU |
|---|---|---|
| Postgres | 768 MiB | 1 CPU |
| blog-api | 256 MiB, sem swap adicional | 1 CPU |
| bolao-api | 256 MiB, sem swap adicional | 1 CPU |
| frontend | 512 MiB | 1 CPU |
| nginx | 128 MiB | 1 CPU |
| cloudflared | 256 MiB | 1 CPU |
Esses valores são tetos, não reservas ou medições de consumo. Os dois limites de API evitam que uma aplicação empurre o restante da máquina para swap; se ela exceder a memória permitida, pode ser encerrada e reiniciada. Reinício automático não corrige um vazamento de memória ou código defeituoso.
Os builds de imagens e os testes principais acontecem nos runners hospedados pelo GitHub. O Aspire baixa imagens prontas e executa os serviços, em vez de compilar toda a aplicação a cada alteração. Isso aproveita melhor o processador antigo e separa o custo de desenvolvimento da execução residencial.
Do código à produção
O repositório no GitHub e seu ramo master são a fonte das alterações implantadas. Um checkout pessoal no Aspire não é, por si só, a versão em execução: os containers usam imagens publicadas pelo pipeline, e o runner tem seu próprio checkout.
O fluxo é:
- Uma alteração é commitada e enviada para
master. - O GitHub executa os checks de cada serviço afetado e constrói sua imagem.
- A imagem é publicada no GHCR, identificada pelo último commit que alterou os caminhos daquele serviço.
- O runner privado no Aspire baixa as imagens que mudaram.
- Antes de migrações de API, é feito um dump dos bancos; em seguida, cada aplicação executa sua cadeia Alembic.
- O serviço é atualizado e precisa passar no health check. Se não ficar saudável, o deploy tenta restaurar sua imagem anterior.
- Uma etapa final aplica a configuração do conjunto, quando todos os serviços previstos tiveram sucesso.
Cada serviço evolui independentemente. Alterar somente o bolão não exige reiniciar o blog; uma falha em um check não força a atualização daquele serviço. Mudanças no código compartilhado podem afetar mais de uma API.
Pull requests executam checks, mas não deploy no servidor privado. Um workflow manual permite verificar ou reaplicar a configuração. Migrações de banco não são revertidas automaticamente quando uma imagem volta à versão anterior; por isso, backup e compatibilidade de schema fazem parte da operação.
Publicar o conteúdo do blog é uma etapa distinta de implantar a imagem. Depois que o Markdown novo chega ao servidor na imagem, é necessário sincronizar o post pelo fluxo administrativo. A consolidação desta documentação também despublica os posts substituídos e mantém redirecionamentos de seus URLs para esta página.
Backups e monitoramento
O backup diário executa um script que chama um helper de dump com permissão restrita. Ele inclui os bancos de aplicação e envia o resultado compactado ao Cloudflare R2. A configuração do script mantém objetos de backup por 30 dias. A cópia temporária local é removida depois da tentativa de upload; backups de pré-deploy seguem sua própria política, mantendo os cinco mais recentes.
O dump não inclui tudo que existe no host. Código versionado, arquivos de configuração privados, credenciais, outros serviços e eventuais dados fora do Postgres precisam de estratégia própria. Restaurar o banco exige preparar os usuários e credenciais correspondentes, recuperar o dump e verificar aplicações e migrações; a existência de um arquivo de backup não substitui um procedimento de restauração.
Healthchecks.io acompanha o job de backup e pode alertar se ele falhar ou deixar de enviar a confirmação esperada. Um monitor externo acompanha a disponibilidade do site e das APIs. Dentro do Compose, health checks ajudam o deploy a decidir se um serviço iniciou corretamente; restart: unless-stopped reinicia containers conforme a política configurada.
Os endpoints públicos atuais são:
/para a entrada do site./api/v1/blog/healthpara a API do blog e seu banco./api/v1/bolao/healthpara a API do bolão e seu banco.
O nginx tem ainda seu próprio check interno, em uma porta não publicada. Um HTTP 200 na página inicial não prova que o banco do blog esteja saudável; um check local da API não prova que DNS e túnel estejam funcionando para visitantes. Cada observação cobre uma parte do caminho.
Como investigar sem confundir as camadas
Uma pessoa ou agente deve começar identificando onde está: navegador público, workstation com acesso autorizado, shell no Aspire ou container. Depois, verificar o caminho em partes:
- DNS: o domínio resolve para a Cloudflare? A delegação continua correta?
- Entrada pública: o domínio responde por HTTPS? Um erro pode estar na edge, na rota do túnel ou na origem.
- Origem local: no Aspire,
http://127.0.0.1:18082responde? Se responde localmente e falha publicamente, investigar o caminho externo antes de atribuir a falha ao frontend. - Aplicação: o endpoint de saúde da aplicação responde? Sua falha pode ser isolada de outras aplicações.
- Banco: o health check confirma conectividade? Logs e migrações precisam ser examinados pelo contexto autorizado.
- Deploy: o workflow terminou? Qual imagem foi implantada, qual falhou e qual foi restaurada? O HEAD de um checkout pessoal não responde sozinho a isso.
Não publicar saídas completas de arquivos de ambiente, logs ou processos para explicar uma falha. Coletar apenas o dado necessário e remover valores privados. Não editar containers diretamente para fazer uma correção duradoura: a próxima implantação pode substituí-los. Mudanças de código passam pelo repositório; operações do host e segredos dependem das permissões administrativas apropriadas.
Novas aplicações precisam ser integradas explicitamente ao registro de serviços, CI, Compose, roteamento, saúde, segredos e persistência. Uma aplicação Node.js é possível neste host, mas não aparece em produção apenas porque foi planejada. A proposta de chatbot WhatsApp está em planejamento; os serviços descritos acima são os que existem neste projeto agora.
O que o rodapé quer dizer
O percurso pode ser resumido assim: domínio registrado, DNS delegado, conexão pública na Cloudflare, túnel de saída, proxy interno, aplicação e banco no Aspire. O acesso administrativo segue uma rede privada diferente do caminho público. O deployment segue o GitHub até um runner autorizado no servidor.
“Hospedado em casa” é a descrição literal dessa origem residencial, com suas limitações e escolhas. A Cloudflare torna essa origem alcançável sem exigir que o navegador descubra ou acesse a rede doméstica. O Aspire continua sendo a máquina que serve o conteúdo; o túnel, o domínio, os backups externos e a operação automatizada tornam esse pequeno servidor parte de um sistema maior.