Deploy técnico (self-host)

Suba a Next Wiki com Docker — variáveis de ambiente, banco, storage e IA.

Self-host com Docker

A Next Wiki roda como uma única imagem (o frontend é servido pelo próprio backend .NET, na mesma porta). Você só precisa de Docker e um PostgreSQL acessível. A configuração é feita por variáveis de ambiente padronizadas (SECRET_KEY, DATABASE_URL, URL, REDIS_URL, SMTP_*…).

Pré-requisitos

  • Docker 24+ (e Docker Compose v2)
  • PostgreSQL 14+ — pode ser um banco já existente; a Next Wiki usa um database próprio (ex.: next_wiki) e nunca dropa o banco (migrations com IF NOT EXISTS).
  • Um endpoint de IA compatível com OpenAI (opcional, mas recomendado): OpenAI, um gateway litellm ou Ollama local.

Requisitos de hardware

A imagem é enxuta e a aplicação reusa um pool de conexão “quente” com o banco — roda confortável em máquinas pequenas.

CenárioCPURAMDisco
Mínimo (time pequeno)1 vCPU1 GB10 GB
Recomendado (app + Postgres na mesma VM)2 vCPU2 GB20 GB+
Equipe grande / muitos anexos2–4 vCPU4 GBconforme o conteúdo

Mantenha backend e PostgreSQL na mesma região/rede. Cada query paga a latência de rede até o banco; com eles juntos, as consultas ficam em poucos milissegundos.

Variáveis de ambiente

VariávelObrigatóriaDescrição
SECRET_KEYsimSegredo que assina o JWT da sessão. ≥ 32 caracteres.
DATABASE_URLsimConexão Postgres: postgresql://usuario:senha@host:5432/next_wiki.
URLsimURL pública da wiki (usada em links de convite/e-mail).
PORTnãoPorta que a aplicação escuta. Padrão 8080.
BOOTSTRAP_ADMIN_EMAILnãoE-mail que vira o 1º admin no primeiro boot (via magic link).
FILE_STORAGE_LOCAL_ROOT_DIRnãoRaiz do storage local (markdown + ícones). Padrão /app/data.
REDIS_URLnãoCache distribuído. Vazio = cache em memória. Ex.: redis://host:6379.
SMTP_HOSTSMTP_SECUREnãoEnvio de e-mail (magic link, convites). Sem SMTP_HOST, e-mail vira no-op.
OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRETnãoSSO via OIDC (Keycloak, etc). Vazio = só magic link.
IA_MODELO, LITELLM_KEYnãoModelo padrão e chave do endpoint de IA (OpenAI-compat).
OTEL_EXPORTER_OTLP_ENDPOINTnãoObservabilidade OpenTelemetry. Vazio = desligado.

docker-compose.yml

services:
  app:
    image: nexttag/next-wiki:latest
    environment:
      SECRET_KEY: "troque-por-um-segredo-com-mais-de-32-chars"
      DATABASE_URL: "postgresql://admin:[email protected]:5432/next_wiki"
      URL: "https://wiki.suaempresa.com"
      BOOTSTRAP_ADMIN_EMAIL: "[email protected]"
      # IA (opcional) — endpoint OpenAI-compat (litellm/OpenAI/Ollama)
      IA_MODELO: "gpt-4o-mini"
      LITELLM_KEY: "sua-chave"
    ports:
      - "5210:8080"                   # host:container
    volumes:
      - nextwiki_data:/app/data       # markdown dos documentos + ícones
    extra_hosts:
      - "host.docker.internal:host-gateway"

volumes:
  nextwiki_data:

Suba com:

docker compose up -d

Sem um Postgres próprio? Aponte DATABASE_URL para um banco existente e crie só o database: CREATE DATABASE next_wiki;. As migrations rodam no boot.

Primeiro acesso

  1. Abra a URL configurada.
  2. Informe o e-mail definido em BOOTSTRAP_ADMIN_EMAIL — ele recebe o magic link (ou, sem SMTP, o link aparece nos logs do container).
  3. Ao entrar, você já é admin do workspace. Convide o time e organize as coleções.

HTTPS e proxy reverso

A imagem serve HTTP na porta interna — em produção, coloque-a atrás de um proxy com TLS. A aplicação já honra os cabeçalhos X-Forwarded-*, então basta o proxy encaminhá-los; os links e cookies de sessão saem com o esquema https corretamente.

Caddy (TLS automático via Let’s Encrypt):

wiki.suaempresa.com {
    reverse_proxy localhost:5210
}

Cloudflare Tunnel (sem abrir portas):

ingress:
  - hostname: wiki.suaempresa.com
    service: http://localhost:5210
  - service: http_status:404

Defina sempre a variável URL com o endereço público https — é ela que monta os links de convite/e-mail e valida a origem das requisições.

Verificar se está no ar

# deve responder 200 (a raiz serve o app)
curl -I https://wiki.suaempresa.com

# acompanhar o boot e as migrations
docker compose logs -f app

Para um healthcheck no Docker, adicione ao serviço app:

    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/"]
      interval: 30s
      timeout: 5s
      retries: 3

Storage e dados

O corpo dos documentos é guardado como markdown em FILE_STORAGE_LOCAL_ROOT_DIR (padrão /app/data), junto dos ícones. Mantenha esse diretório em um volume para não perder conteúdo entre atualizações. Os metadados (usuários, permissões, árvore) ficam no PostgreSQL.

Produção: escala e segurança

  • Rate limiting já vem embutido — a aplicação limita requisições por padrão, sem configuração extra.
  • Cabeçalhos de segurança e compressão de resposta também são aplicados automaticamente.
  • Edição em tempo real: a colaboração usa WebSocket (SignalR). Para uma instância, funciona sem nada a mais. Para rodar várias instâncias atrás de um balanceador, configure REDIS_URL — o Redis vira o backplane que sincroniza as conexões entre os nós (e também serve de cache distribuído).
  • Observabilidade: defina OTEL_EXPORTER_OTLP_ENDPOINT para enviar logs/traces (OpenTelemetry) ao seu coletor (Grafana/Loki, etc).

Atualizar

# 1. backup do banco e do volume (ver abaixo)
docker compose pull
docker compose up -d        # migrations rodam automaticamente no boot

Backup

# Banco (metadados)
pg_dump "postgresql://admin:senha@localhost:5432/next_wiki" > next_wiki.sql

# Conteúdo (markdown + ícones)
docker run --rm -v nextwiki_data:/data -v "$PWD":/backup alpine \
  tar czf /backup/nextwiki_data.tgz -C /data .

Solução de problemas

SintomaCausa provávelO que fazer
Container não sobe e someSECRET_KEY ausente/curtoDefina SECRET_KEY com ≥ 32 caracteres.
password authentication failed / sem conexãoDATABASE_URL errada ou banco inacessívelConfira host/porta/credenciais; de dentro do container use host.docker.internal.
Não chega o magic linkSMTP não configuradoSem SMTP_HOST, o link aparece nos logs (docker compose logs app).
Login cai/redireciona errado atrás de proxyURL ou X-Forwarded-*Use URL com https público e garanta que o proxy encaminha os cabeçalhos.
Conteúdo sumiu após atualizarvolume não persistidoO /app/data precisa estar em um volume nomeado.
IA não respondeendpoint/chave de IAVerifique IA_MODELO e a chave; o endpoint precisa ser compatível com OpenAI.

Pronto — uma instância sua, com os dados sob seu controle e a IA apontando para o provedor que você escolher.