Criado em:
Atualizado em:

Texto gerado por IA.

ai-generatedself-hostingredesinfraestrutura

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:

ItemConfiguração verificada
ProcessadorIntel Core i5-2410M, 2 núcleos e 4 threads, frequência nominal de 2,30 GHz
Memória10 GB instalados; o Linux apresenta aproximadamente 9,6 GiB utilizáveis
Sistema operacionalLinux Mint 22.3, Zena, baseado em Ubuntu 24.04
KernelLinux 6.8.0-124-generic, arquitetura x86-64
Inicializaçãomulti-user.target; gerenciador de login gráfico inativo
Disco do sistemaSSD Crucial de 240 GB nominais; nele está montado /
Armazenamento adicionalHDD Samsung de 640 GB e SSD Western Digital de 120 GB nominais
Conexão utilizadaEthernet; Wi-Fi estava inativo na inspeção
Fuso horárioAmerica/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.

ContextoComo alcançar o site ou serviço
Visitante na internethttps://jpsalviano.com.br, pela Cloudflare
Shell no próprio Aspirehttp://127.0.0.1:18082, entrada local do nginx
Administrador autorizadoSSH pelo nome aspire, conforme a configuração privada de Tailscale e SSH
Container do frontendhttp://blog-api:8000 ou http://bolao-api:8000, por DNS interno do Docker
API dentro do Composepostgres:5432, com o usuário e banco exclusivos da aplicação
Navegador do visitanteURLs 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:

ResponsabilidadePapel neste projeto
Registro do domínioRegistro.br, dentro da estrutura do domínio .br administrada pelo NIC.br
TitularResponsável por manter o domínio registrado e suas configurações
DNS autoritativoCloudflare, após delegação dos nameservers no Registro.br
Proxy HTTPS e entrada públicaCloudflare, integrada ao túnel
Origem da aplicaçãoAspire, 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:

RedeParticipantesFunção
internalPostgres, APIs, frontend e nginxComunicação entre serviços; configurada com internal: true, sem saída direta para a internet
edgenginx e cloudflaredConexã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:

CaminhoResponsável
/Página inicial com acesso ao blog e ao bolão
/blogArquivo público do blog
/blog/posts/<slug>Conteúdo de um post
/blog/adminAdministração autenticada do blog
/bolaoClassificações públicas por campeonato e ranking geral de tipsters
/bolao/palpitesHistó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çoLimite de memóriaLimite de CPU
Postgres768 MiB1 CPU
blog-api256 MiB, sem swap adicional1 CPU
bolao-api256 MiB, sem swap adicional1 CPU
frontend512 MiB1 CPU
nginx128 MiB1 CPU
cloudflared256 MiB1 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 é:

  1. Uma alteração é commitada e enviada para master.
  2. O GitHub executa os checks de cada serviço afetado e constrói sua imagem.
  3. A imagem é publicada no GHCR, identificada pelo último commit que alterou os caminhos daquele serviço.
  4. O runner privado no Aspire baixa as imagens que mudaram.
  5. Antes de migrações de API, é feito um dump dos bancos; em seguida, cada aplicação executa sua cadeia Alembic.
  6. O serviço é atualizado e precisa passar no health check. Se não ficar saudável, o deploy tenta restaurar sua imagem anterior.
  7. 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/health para a API do blog e seu banco.
  • /api/v1/bolao/health para 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:

  1. DNS: o domínio resolve para a Cloudflare? A delegação continua correta?
  2. Entrada pública: o domínio responde por HTTPS? Um erro pode estar na edge, na rota do túnel ou na origem.
  3. Origem local: no Aspire, http://127.0.0.1:18082 responde? Se responde localmente e falha publicamente, investigar o caminho externo antes de atribuir a falha ao frontend.
  4. Aplicação: o endpoint de saúde da aplicação responde? Sua falha pode ser isolada de outras aplicações.
  5. Banco: o health check confirma conectividade? Logs e migrações precisam ser examinados pelo contexto autorizado.
  6. 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.