Deploy técnico (self-host)

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

Self-host com Docker

A Next Wiki roda numa imagem única (nexttag/next-wiki no Docker Hub): o frontend é servido pelo próprio backend .NET, na mesma porta. Você precisa de Docker e de um PostgreSQL. Redis, Qdrant, SMTP e MinIO são opcionais.

Pré-requisitos

  • Docker 24+ (e Docker Compose v2).
  • PostgreSQL 14+. Pode ser um banco que você já tem: a Next Wiki usa um database próprio (ex.: next_wiki), roda as migrations sozinha no boot e nunca apaga o banco.
  • Um provedor de IA (opcional, mas é o que dá sentido ao produto): OpenAI, Google Gemini, OpenRouter, um gateway litellm ou Ollama local. Ele é configurado dentro do app, não no servidor.

Requisitos de hardware

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

A IA roda no provedor que você escolher, não nessa máquina — salvo se você usar Ollama no mesmo servidor, e aí quem pesa é o modelo.

Mantenha a aplicação e o PostgreSQL na mesma rede. Cada consulta paga a latência até o banco; com os dois perto, essa ida e volta leva poucos milissegundos.

docker-compose.yml

services:
  app:
    image: nexttag/next-wiki:latest
    restart: unless-stopped
    environment:
      SECRET_KEY: "troque-por-um-segredo-com-mais-de-32-caracteres"
      DATABASE_URL: "postgresql://admin:[email protected]:5432/next_wiki"
      URL: "https://wiki.suaempresa.com"
      BOOTSTRAP_ADMIN_EMAIL: "[email protected]"
      # Sem SSO? Deixe estas duas declaradas e VAZIAS (veja "Login único" abaixo).
      OIDC_ISSUER_URL: ""
      OIDC_CLIENT_ID: ""
    ports:
      - "5210:8080"                   # host:container
    volumes:
      - nextwiki_data:/app/data       # documentos, anexos, ícones e chaves de sessão
    extra_hosts:
      - "host.docker.internal:host-gateway"

volumes:
  nextwiki_data:
docker compose up -d

Para fixar uma versão em vez de latest, use a tag dela (ex.: nexttag/next-wiki:1.4.0).

Variáveis de ambiente

Essenciais

VariávelObrigatóriaDescrição
SECRET_KEYsimAssina a sessão. 32 caracteres ou mais; sem ela a aplicação não sobe.
DATABASE_URLsimpostgresql://usuario:senha@host:5432/next_wiki.
URLrecomendadaEndereço público, com https. Monta os links de convite e de e-mail e define a origem das passkeys.
BOOTSTRAP_ADMIN_EMAILno 1º bootE-mail que vira o primeiro admin no primeiro login.
PORTnãoPorta interna. Padrão 8080. Se mudar, o lado direito de ports acompanha (5210:9000 com PORT=9000).
FILE_STORAGE_LOCAL_ROOT_DIRnãoPasta dos dados locais. Padrão /app/data.

E-mail

VariávelDescrição
SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORDServidor SMTP. Porta padrão 587.
SMTP_FROM_EMAIL, SMTP_REPLY_EMAIL, SMTP_SECURERemetente, resposta e TLS.

Sem SMTP_HOST, nenhum e-mail sai. O link de login aparece no log do container (com URL preenchida; sem ela, o log traz só o token), e o link do convite você copia na tela de Membros. O link de login dura 15 minutos, com até 5 pedidos por hora. Qualquer provedor com SMTP serve.

Login único (OIDC / Keycloak)

VariávelDescrição
OIDC_ISSUER_URLEmissor do realm, ex.: https://auth.suaempresa.com/realms/empresa.
OIDC_CLIENT_ID, OIDC_CLIENT_SECRETO cliente da wiki no Keycloak.
OIDC_AUTH_URI, OIDC_LOGOUT_URIAlternativa ao emissor (ele é derivado do endereço de autorização) e logout.
KEYCLOAK_APPS_PERMITIDOSClientes do Keycloak, separados por vírgula, que podem usar o próprio token na API da conversa (ex.: o app de mesa).
KEYCLOAK_LIMITE_PESSOA_MINUTO, KEYCLOAK_LIMITE_APP_MINUTOPedidos por minuto desses apps: por pessoa (padrão 30) e por app (padrão 300).
MOSTRAR_LOGIN_SEM_SSOtrue mostra e-mail e passkey na tela de login mesmo com SSO. Por padrão, com SSO, só o SSO aparece.

Tudo isso também pode ser feito pela tela, em Configurações → Authentication → Keycloak, e o valor da tela tem prioridade sobre o do ambiente. Veja o manual de administração.

⚠️ Ausente não é desligado. Sem as variáveis OIDC_*, a imagem usa o Keycloak de fábrica do appsettings.json, e a tela de login passa a mostrar só o botão de SSO. Se você não usa SSO, declare OIDC_ISSUER_URL: "" e OIDC_CLIENT_ID: "" — variável presente e vazia desliga. Confira em Authentication → Keycloak: cada campo diz de onde veio o valor.

Opcionais de infraestrutura

VariávelDescrição
REDIS_URLCache distribuído (árvore, embeds, checagem de versão). Vazio = cache em memória.
Embeddings__Providerqdrant liga a busca semântica. Padrão: desligada.
Qdrant__Host, Qdrant__Port, Qdrant__Https, Qdrant__ApiKeyConexão inicial do Qdrant (a tela pode trocar depois).
Blob__Backendlocal (padrão) ou minio para guardar anexos e ícones num S3 compatível. Com minio: Blob__Endpoint, Blob__AccessKey, Blob__SecretKey, Blob__Bucket, Blob__UseSSL.
Ia__WebSearch__ProviderBusca na web para os agentes: tavily, brave ou searxng, com Ia__WebSearch__ApiKey ou Ia__WebSearch__Url. Padrão: desligada.
OTEL_EXPORTER_OTLP_ENDPOINTEnvia logs e traces por OpenTelemetry.
RATE_LIMIT_GLOBAL, RATE_LIMIT_SENSIVELPedidos por minuto por IP: geral (padrão 240) e nas rotas de login, passkey, POST /api/chat e MCP (padrão 12).
TRUSTED_PROXIESProxies, além do loopback, cujo X-Forwarded-For é aceito.

Banco de dados (PostgreSQL)

A Next Wiki guarda no PostgreSQL usuários, permissões, árvore, versões, conversas e configurações. As migrations rodam sozinhas no boot.

Opção A — usar um Postgres que você já tem

Crie só o database e aponte DATABASE_URL para ele:

CREATE DATABASE next_wiki;

De dentro do container, use host.docker.internal como host (como no compose acima).

Opção B — subir o Postgres junto

services:
  app:
    image: nexttag/next-wiki:latest
    restart: unless-stopped
    environment:
      SECRET_KEY: "troque-por-um-segredo-com-mais-de-32-caracteres"
      DATABASE_URL: "postgresql://nextwiki:senha@db:5432/next_wiki"
      URL: "https://wiki.suaempresa.com"
      BOOTSTRAP_ADMIN_EMAIL: "[email protected]"
      OIDC_ISSUER_URL: ""             # sem SSO: declarado e vazio
      OIDC_CLIENT_ID: ""
    ports:
      - "5210:8080"
    volumes:
      - nextwiki_data:/app/data
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: nextwiki
      POSTGRES_PASSWORD: senha
      POSTGRES_DB: next_wiki
    volumes:
      - nextwiki_pg:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U nextwiki -d next_wiki"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  nextwiki_data:
  nextwiki_pg:

Primeiro acesso

  1. Abra a URL configurada.
  2. Entre com o e-mail de BOOTSTRAP_ADMIN_EMAIL. Ele recebe o link de login (sem SMTP, o link aparece em docker compose logs app). Se o SSO estiver configurado, entrar pelo SSO com esse e-mail também serve. Se a tela só mostra o botão de SSO e você não configurou nenhum, veja o aviso em Login único.
  3. Você entra como admin. Configure a IA em Configurações → IA → LLM e convide o time — veja os Primeiros passos.

Inteligência artificial

A IA é configurada por workspace, dentro do app, em Configurações → IA → LLM: provedor (OpenAI, Google Gemini, OpenRouter, litellm ou Ollama), chave e modelo. Tudo fica no banco; não há variável de ambiente para isso. Para usar modelos da Anthropic (Claude), passe pelo OpenRouter ou por um gateway litellm.

Busca semântica (Qdrant)

Para a IA achar documentos pelo sentido, suba um Qdrant no mesmo compose e ligue-o na aplicação:

services:
  app:
    # ...o resto do serviço, como acima
    environment:
      Embeddings__Provider: "qdrant"
      Qdrant__Host: "qdrant"        # o nome do serviço abaixo
      Qdrant__Port: "6334"          # gRPC

  qdrant:
    image: qdrant/qdrant
    restart: unless-stopped
    volumes:
      - nextwiki_qdrant:/qdrant/storage

volumes:
  nextwiki_qdrant:

Depois, em Configurações → IA → Busca semântica, escolha o modelo de embedding (Ollama, Gemini, OpenAI ou outro compatível com OpenAI) e mande reindexar. Sem Qdrant, a wiki funciona normalmente com a busca por texto.

HTTPS e proxy reverso

A imagem serve HTTP. Em produção, coloque-a atrás de um proxy com TLS. A aplicação honra os cabeçalhos X-Forwarded-* vindos de proxies confiáveis (loopback, a rede do Docker e o que você listar em TRUSTED_PROXIES).

Caddy (TLS automático):

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

A conversa e a presença usam WebSocket: o proxy precisa deixá-lo passar (Caddy e Cloudflare Tunnel já deixam).

Verificar se está no ar

# 200 com os métodos de login disponíveis
curl -s https://wiki.suaempresa.com/api/auth/metodos

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

A imagem não traz curl nem wget, e a aplicação não tem rota de healthcheck própria. Monitore de fora, com a chamada acima.

Storage e dados

Em /app/data (o volume) ficam o texto dos documentos em Markdown, os anexos, os ícones e as chaves de proteção de dados (dp-keys). Mantenha esse diretório num volume: sem ele, recriar o container perde o conteúdo. O resto vive no PostgreSQL.

A sessão não depende do volume: ela vale enquanto o SECRET_KEY for o mesmo. Perder dp-keys só interrompe quem estava no meio de um login por SSO ou passkey.

Produção: escala e segurança

  • Limite de pedidos, cabeçalhos de segurança (CSP, HSTS) e compressão já vêm ligados.
  • Uma instância é o recomendado. A conversa, a presença, a fila dos agentes, a indexação e os webhooks ficam na memória da instância que os recebeu; o Redis só compartilha o cache. Para rodar várias, use o mesmo SECRET_KEY, o mesmo volume (ou MinIO) e afinidade de sessão no balanceador — e saiba que uma conversa ou um agente não atravessa de uma instância para outra.
  • Sessão: mantenha o mesmo SECRET_KEY entre reinícios e instâncias. Se ele mudar, todos são desconectados.
  • Contas bloqueadas perdem a sessão e as chaves de API na hora.

Atualizar

# 1. faça o backup (abaixo)
docker compose pull
docker compose up -d        # as migrations rodam no boot

A tela Configurações → Instalação avisa quando há versão nova.

Backup

# Banco — use o usuário, a senha e o host do seu DATABASE_URL
pg_dump "postgresql://admin:senha@localhost:5432/next_wiki" > next_wiki.sql

# Banco, na opção B (Postgres no mesmo compose)
docker compose exec db pg_dump -U nextwiki next_wiki > next_wiki.sql

# Arquivos (documentos, anexos, ícones, chaves)
docker run --rm -v nextwiki_data:/data -v "$PWD":/backup alpine \
  tar czf /backup/nextwiki_data.tgz -C /data .

Os dois precisam vir do mesmo momento: o banco aponta para os arquivos.

Solução de problemas

SintomaCausa provávelO que fazer
Container não sobeSECRET_KEY ausente ou curto, ou DATABASE_URL ausenteDefina os dois; a chave com 32+ caracteres.
password authentication failedDATABASE_URL errada ou banco inacessívelConfira host, porta e credenciais; de dentro do container use host.docker.internal.
O link de login não chegaSem SMTPO link está em docker compose logs app.
Login volta para a tela inicial atrás de proxyURL sem https ou proxy sem X-Forwarded-*Use URL com https e confira TRUSTED_PROXIES.
Botão do SSO não apareceEmissor ou client vazioConfigure em Authentication → Keycloak e veja de onde vem cada valor.
Só aparece o botão de SSO, e você não configurou SSOVariáveis OIDC_* ausentes: entrou o Keycloak de fábricaDeclare OIDC_ISSUER_URL: "" e OIDC_CLIENT_ID: "".
A IA não respondeNenhum provedor ativoAtive um em Configurações → IA → LLM.
Busca semântica vaziaQdrant desligado ou não indexadoEmbeddings__Provider=qdrant e reindexar na tela.
Conteúdo sumiu depois de atualizar/app/data fora de volumeRestaure o backup e use um volume nomeado.