Self-Hosting - Sumário

Introdução

Este é um guia que estou fazendo direcionado a pessoas que querem hospedar sua própria instância do Tabledown, mas obviamente temos inúmeras maneiras de hostear aplicações web atualmente, então nesse guia vou começar com algumas premissas:

Crie Uma VPS Em Qualquer Plataforma De Cloud

Qualquer plataforma de cloud terá pelo menos um serviço, que é uma VPS/Máquina Virtual, é exatamente o que precisamos. Gosto de alugar servidores Debian Linux, pois tendem a ser mais estáveis, mas fique à vontade pra escolher o que quiser.

Uma vez que sua VPS esteja adquirida, você provavelmente vai querer abrir algumas rotas TCP primeiro, isso varia muito com o provedor de cloud, mas geralmente você quer encontrar as regras de segurança de rede e abrir algumas portas:

Protocolo Tipo Porta Direção Remote CIDR
TCP IPv4 80 (HTTP) IN * (Qualquer IP)
TCP IPv4 443 (HTTPS) IN * (Qualquer IP)

Com isso definido, qualquer IP pode acessar nossas portas para HTTP e HTTPS.

Agora você vai anotar seu IP público YOUR_VPS_IP, também configure suas chaves SSH para se conectar à sua VPS quando necessário;

Conecte-se via SSH para confirmar que você já tem acesso à sua nova máquina (no exemplo abaixo estou usando root como usuário, mas isso pode variar com o provedor de cloud):

ssh root@YOUR_VPS_IP

Aponte Seu Domínio Para A VPS

Você pode pular esta etapa se não quiser ter um domínio para seu servidor nem certificados SSL, mas caso queira, você precisará ir ao registrar de domínios da sua preferência, comprar o domínio que deseja (usarei mydomain.com como referência) e nas configurações do seu domínio no registrar você precisa encontrar onde gerenciar DNS / Nameservers. Então você definirá o DNS do seu domínio para apontar para o IP público da sua VPS.

Adicione estes registros:

Tipo Nome Valor TTL
A @ YOUR_VPS_IP 300
A api YOUR_VPS_IP 300
A www YOUR_VPS_IP 300

O @ cobre mydomain.com e www cobre www.mydomain.com. O subdomínio api cobre api.mydomain.com que usaremos para o backend.

Feito isso, precisamos esperar a propagação do DNS, o que pode levar de alguns minutos a algumas horas.

Preparar A VPS

Supondo que você esteja usando um sistema baseado em Debian para sua VPS, primeiro precisamos atualizar o sistema e depois instalar alguns pacotes:

apt update && apt upgrade -y
apt install -y nginx certbot python3-certbot-nginx docker.io docker-compose git ufw

Com isso temos a maior parte do que usaremos para hospedar o servidor:

Agora configuramos os firewalls para SSH e Nginx:

ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable

E finalmente iniciamos os serviços do Nginx e Docker:

systemctl enable nginx docker
systemctl start nginx docker

Subindo Sua Aplicação

Agora é hora de clonar o Tabledown na VPS, usarei o repositório principal do Tabledown como exemplo, mas você pode usar seu próprio fork também, obviamente, também clonarei o Tabledown na pasta /opt/tabledown, mas isso é 100% preferência, você pode cloná-lo onde quiser em sua VPS.

Observe também que usarei a versão HTTP do repositório, mas sinta-se à vontade para usar a versão SSH e configurar chaves SSH para sua origem remota do Git em sua VPS.

git clone https://codeberg.org/grepehu/tabledown.git /opt/tabledown
cd /opt/tabledown

docker compose up -d

Variáveis De Ambiente

Agora com o Tabledown clonado, precisamos apenas configurar as variáveis de ambiente adequadamente. No projeto principal temos o .env.example, você pode apenas copiar e colar em um novo arquivo .env e então substituir as variáveis pelos valores apropriados:

Fichas

Tocamos neste assunto na seção anterior, mas gostaria de ressaltar que VITE_SHEETS_URL é uma variável que aponta para um endereço onde a aplicação espera encontrar um arquivo sheets.json, este endereço pode apontar para qualquer lugar inclusive para dentro da própria aplicação, o objetivo disso e permitir que as fichas sejam atualizadas constantemente sem a necessidade de reconstruir a aplicação pra todo update.

Além disso você pode obter uma versão atual de fichas que usamos em produção em https://codeberg.org/grepehu/tabledown-sheets/src/branch/master/sheets.json, onde você pode baixar este arquivo, adicionar em src/public/ no seu projeto e colocar a variável VITE_SHEETS_URL como /sheets.json, fazendo assim sua aplicação obter as fichas dentro de si mesma.

Gerenciamento Docker

Como o Docker já está instalado no sistema e tendo nosso .env configurado, podemos agora iniciar nossos contêineres com (dentro da pasta tabledown, onde temos o arquivo docker-compose.yml):

docker compose up -d

Isso iniciará um servidor em segundo plano, para futuras atualizações, o que gosto de fazer pessoalmente é puxar as mudanças recentes no git, desligar os contêineres Docker, limpar qualquer coisa dentro do sistema Docker (isso é opcional, mas gosto de fazer para evitar o cache e logs que se acumulam muito rapidamente no Docker) e finalmente iniciar o contêiner novamente:

git pull
docker compose down
docker system prune -a
docker compose up -d

Configurar Nginx

Para lidar com nosso proxy reverso no Nginx, precisaremos criar dois arquivos de configuração:

/etc/nginx/sites-available/mydomain.com

server {
    listen 80;
    server_name mydomain.com www.mydomain.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

/etc/nginx/sites-available/api.mydomain.com

server {
    listen 80;
    server_name api.mydomain.com;

    location / {
        proxy_pass http://127.0.0.1:3000;

        # Necessário para SSE
        proxy_buffering off;
        proxy_cache off;
        proxy_http_version 1.1;
        proxy_set_header Connection '';

        # Timeouts longos o suficiente para SSE
        proxy_read_timeout 24h;
        proxy_send_timeout 24h;
        keepalive_timeout 24h;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Observe que especialmente para o backend temos algumas linhas especiais por causa das conexões SSE em tempo real que usamos na aplicação, como timeouts grandes e desativação de buffer.

Tendo esses arquivos escritos, precisamos habilitá-los e recarregar o Nginx:

ln -s /etc/nginx/sites-available/tabledown.net /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/api.tabledown.net /etc/nginx/sites-enabled/

Você pode remover o placeholder antigo do Nginx se quiser:

rm /etc/nginx/sites-enabled/default

Então verificamos se há erros em nossos arquivos de configuração:

nginx -t

E finalmente recarregamos o Nginx:

systemctl reload nginx

SSL Com Let's Encrypt

Quando o DNS tiver se propagado para o IP da sua VPS, podemos ativar o SSL, podemos verificar se o DNS se propagou com:

dig mydomain.com

Se for bem-sucedido, podemos finalmente usar o Certbot para adicionar certificados SSL ao nosso domínio com:

certbot --nginx -d mydomain.com -d www.mydomain.com
certbot --nginx -d api.mydomain.com

O Certbot modificará automaticamente suas configurações do Nginx para adicionar HTTPS e configurar um bloco de servidor 443. Ele também instala um cron job para renovar automaticamente os certificados.

Podemos sempre simular se a renovação do SSL funciona executando um dry test:

certbot renew --dry-run

Quando o SSL estiver pronto, você talvez queira adicionar ao frontend do Nginx uma maneira de redirecionar www. para removê-lo, então no bloco SSL 443 adicione isto abaixo dos nomes de domínio:

    if ($host = www.mydomain.com) {
        return 301 https://mydomain.com$request_uri; # Redireciona www para removê-lo
    }

O trecho acima é opcional, mas importante porque o Tabledown usa local storage para salvar dados do usuário e os navegadores salvam o local storage com base no domínio do site, então usuários acessando www.mydomain.com terão dados diferentes no armazenamento do que mydomain.com.

Quando tudo estiver feito, recarregamos o Nginx:

nginx -t && systemctl reload nginx

Pronto

Agora você provavelmente conseguirá acessar sua aplicação em mydomain.com e ela se comunicará corretamente com o backend em api.mydomain.com.

Apenas uma observação, a configuração do docker-compose para esta aplicação está definida para não salvar nenhum dado das execuções dos contêineres, então a cada reinicialização você perderá todas as salas que estavam lá, o que não é tão ruim considerando que o Tabledown exclui salas a cada 24 horas de qualquer forma, mas se você quiser mudar esse comportamento, você pode modificar o docker-compose.yml como achar melhor.