Seu próprio servidor de sincronização

O Polypodium funciona 100% offline — o servidor só é necessário se você quiser sincronizar sua coleção entre vários aparelhos. Este guia mostra como subir o servidor com Docker em poucos minutos, sem precisar compilar nada.

O que você vai precisar

Não é preciso clonar repositório nem instalar Dart: o servidor é distribuído como imagem Docker pronta em ghcr.io/bruno1pb13/polypodium_server.

1 Criar os arquivos de configuração

Crie uma pasta (por exemplo polypodium-server) e, dentro dela, um arquivo docker-compose.yml com o conteúdo abaixo. Ele sobe dois contêineres: o banco PostgreSQL e o servidor do Polypodium.

services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: polypodium
      POSTGRES_USER: polypodium
      POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U polypodium -d polypodium"]
      interval: 5s
      timeout: 5s
      retries: 10

  server:
    image: ghcr.io/bruno1pb13/polypodium_server:latest
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      DATABASE_URL: postgresql://polypodium:${DB_PASSWORD}@db/polypodium
      JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required}
      APP_ENV: production
      DB_SSL: "false"
      # O HTTPS fica a cargo do proxy reverso (passo 3).
      BEHIND_PROXY: "true"
      REGISTRATION_TOKEN: ${REGISTRATION_TOKEN:-}
      PHOTOS_DIR: /photos
    volumes:
      - photos_data:/photos
    depends_on:
      db:
        condition: service_healthy

volumes:
  db_data:
  photos_data:

Na mesma pasta, crie um arquivo .env com os segredos do seu servidor:

# Senha do banco de dados (usada só internamente, entre os contêineres)
DB_PASSWORD=troque-por-uma-senha-forte

# Chave que assina os tokens de login. Mínimo de 32 caracteres.
# Gere uma com:  openssl rand -base64 48
JWT_SECRET=troque-por-uma-chave-aleatoria-bem-longa

# Opcional, recomendado se o servidor ficar exposto na internet:
# protege a criação da primeira conta (veja o passo 4).
REGISTRATION_TOKEN=
Importante: o servidor se recusa a iniciar em produção se o JWT_SECRET estiver ausente ou tiver menos de 32 caracteres. Guarde o .env em local seguro — quem tem esses valores tem acesso ao servidor.

2 Subir o servidor

Ainda na pasta criada, execute:

docker compose up -d

Na primeira execução o Docker baixa as imagens e o servidor cria as tabelas do banco automaticamente. Para conferir que está tudo no ar:

curl http://localhost:8080/api/v1/health

Se a resposta chegar com status 200, o servidor está funcionando. Para acompanhar os logs a qualquer momento: docker compose logs -f server.

Só na rede local? Se o servidor vai ser acessado apenas dentro da sua casa (ex.: http://192.168.0.10:8080), você já pode pular para o passo 4. O HTTPS do passo 3 é essencial quando o servidor fica acessível pela internet.

3 HTTPS com proxy reverso (Let's Encrypt)

A configuração acima (BEHIND_PROXY: "true") espera que um proxy reverso na frente do servidor cuide do HTTPS. A forma mais simples é usar o Caddy, que obtém e renova certificados Let's Encrypt sozinho. Com o domínio já apontando para a máquina, instale o Caddy e use um Caddyfile de duas linhas:

plantas.seudominio.com {
    reverse_proxy 127.0.0.1:8080
}

Pronto: o app passa a acessar https://plantas.seudominio.com. Alternativas que funcionam igualmente bem, com o mesmo BEHIND_PROXY=true:

Nesse arranjo, deixe a porta 8080 fechada para a internet no firewall — só o proxy (portas 80/443) precisa ficar exposto.

Alternativa: o próprio servidor termina o HTTPS

Se preferir não usar proxy, o servidor pode servir HTTPS diretamente. No docker-compose.yml, troque BEHIND_PROXY: "true" por:

      BEHIND_PROXY: "false"
      SSL_CERT_PATH: /certs/fullchain.pem
      SSL_KEY_PATH: /certs/privkey.pem

e monte a pasta dos certificados no contêiner (em volumes: do serviço server):

      - /caminho/para/seus/certs:/certs:ro

Nesse caso a renovação dos certificados (ex.: via certbot) fica por sua conta. Em produção o servidor exige uma das duas opções — proxy reverso ou certificados — e se recusa a iniciar servindo HTTP puro na internet.

4 Criar a primeira conta (administrador)

A primeira conta criada em um servidor novo vira automaticamente a conta de administrador. Depois dela, o cadastro público se fecha: novas contas só podem ser criadas pelo administrador, dentro do app.

Sem REGISTRATION_TOKEN (rede local)

  1. No app, abra Configurações → Servidor (workspaces) e adicione um workspace remoto com a URL do seu servidor.
  2. O app detecta que o servidor ainda não tem contas e oferece a criação da primeira — informe e-mail e senha (mínimo de 8 caracteres) e pronto: você é o admin.

Com REGISTRATION_TOKEN (recomendado na internet)

Se você definiu REGISTRATION_TOKEN no .env, a primeira conta precisa apresentar esse token — assim ninguém "rouba" o posto de admin chegando primeiro no seu servidor recém-instalado. Crie a conta com uma chamada direta à API:

curl -X POST https://plantas.seudominio.com/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "voce@exemplo.com",
    "password": "sua-senha-com-8+-caracteres",
    "registrationToken": "o-valor-do-seu-REGISTRATION_TOKEN"
  }'

Depois disso, basta fazer login normalmente pelo app com o e-mail e a senha criados. As próximas contas (família, amigos) você cria pela tela de administração do servidor, dentro do próprio app.

5 Backups

Seus dados vivem em dois lugares: o banco PostgreSQL (plantas, espécies, diário…) e o volume de fotos. Faça backup dos dois periodicamente.

Banco de dados (pg_dump)

docker compose exec db pg_dump -U polypodium -d polypodium \
  > backup-polypodium-$(date +%F).sql

Para restaurar em um servidor recém-criado (com o banco ainda vazio):

docker compose exec -T db psql -U polypodium -d polypodium \
  < backup-polypodium-2026-07-10.sql

Fotos

docker compose cp server:/photos ./backup-fotos-$(date +%F)

Automatize com um cron semanal e guarde as cópias em outra máquina ou em um serviço de armazenamento — um backup que mora no mesmo disco do servidor não protege contra a falha desse disco.

Atualizando o servidor

Para atualizar para a versão mais recente da imagem:

docker compose pull
docker compose up -d

As migrações do banco rodam automaticamente na inicialização — não há passo manual.

Referência: variáveis de ambiente

VariávelPara que serve
DB_PASSWORDSenha do PostgreSQL (obrigatória).
JWT_SECRETAssina os tokens de login. Obrigatória, mínimo de 32 caracteres, única por servidor.
REGISTRATION_TOKENOpcional. Quando definida, a criação da primeira conta exige esse token (passo 4).
BEHIND_PROXYtrue quando o HTTPS termina em um proxy reverso; senão, informe SSL_CERT_PATH/SSL_KEY_PATH.
SSL_CERT_PATH / SSL_KEY_PATHCertificado e chave para o servidor servir HTTPS diretamente.
ALLOWED_ORIGINSCORS: * ou lista de origens separadas por vírgula. Padrão: *.
AUTH_RATE_LIMIT_MAX / AUTH_RATE_LIMIT_WINDOWLimite contra força bruta no login. Padrão: 20 requisições / 300 s por IP.
MAX_JSON_BODY_BYTES / MAX_PHOTO_BYTESTamanho máximo de requisições e fotos. Padrão: 1 MB / 15 MB.

Dúvidas ou problemas? Abra uma issue no repositório do servidor ou escreva para bruno1pb13@gmail.com.