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ário | CPU | RAM | Disco |
|---|---|---|---|
| Mínimo (time pequeno) | 1 vCPU | 1 GB | 10 GB |
| Recomendado (app + Postgres na mesma VM) | 2 vCPU | 2 GB | 20 GB+ |
| Equipe grande / muitos anexos | 2–4 vCPU | 4 GB | conforme 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:senha@host.docker.internal:5432/next_wiki"
URL: "https://wiki.suaempresa.com"
BOOTSTRAP_ADMIN_EMAIL: "voce@suaempresa.com"
# 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ável | Obrigatória | Descrição |
|---|---|---|
SECRET_KEY | sim | Assina a sessão. 32 caracteres ou mais; sem ela a aplicação não sobe. |
DATABASE_URL | sim | postgresql://usuario:senha@host:5432/next_wiki. |
URL | recomendada | Endereço público, com https. Monta os links de convite e de e-mail e define a origem das passkeys. |
BOOTSTRAP_ADMIN_EMAIL | no 1º boot | E-mail que vira o primeiro admin no primeiro login. |
PORT | não | Porta interna. Padrão 8080. Se mudar, o lado direito de ports acompanha (5210:9000 com PORT=9000). |
FILE_STORAGE_LOCAL_ROOT_DIR | não | Pasta dos dados locais. Padrão /app/data. |
| Variável | Descrição |
|---|---|
SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD | Servidor SMTP. Porta padrão 587. |
SMTP_FROM_EMAIL, SMTP_REPLY_EMAIL, SMTP_SECURE | Remetente, 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ável | Descrição |
|---|---|
OIDC_ISSUER_URL | Emissor do realm, ex.: https://auth.suaempresa.com/realms/empresa. |
OIDC_CLIENT_ID, OIDC_CLIENT_SECRET | O cliente da wiki no Keycloak. |
OIDC_AUTH_URI, OIDC_LOGOUT_URI | Alternativa ao emissor (ele é derivado do endereço de autorização) e logout. |
KEYCLOAK_APPS_PERMITIDOS | Clientes 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_MINUTO | Pedidos por minuto desses apps: por pessoa (padrão 30) e por app (padrão 300). |
MOSTRAR_LOGIN_SEM_SSO | true 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 doappsettings.json, e a tela de login passa a mostrar só o botão de SSO. Se você não usa SSO, declareOIDC_ISSUER_URL: ""eOIDC_CLIENT_ID: ""— variável presente e vazia desliga. Confira em Authentication → Keycloak: cada campo diz de onde veio o valor.
Opcionais de infraestrutura
| Variável | Descrição |
|---|---|
REDIS_URL | Cache distribuído (árvore, embeds, checagem de versão). Vazio = cache em memória. |
Embeddings__Provider | qdrant liga a busca semântica. Padrão: desligada. |
Qdrant__Host, Qdrant__Port, Qdrant__Https, Qdrant__ApiKey | Conexão inicial do Qdrant (a tela pode trocar depois). |
Blob__Backend | local (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__Provider | Busca na web para os agentes: tavily, brave ou searxng, com Ia__WebSearch__ApiKey ou Ia__WebSearch__Url. Padrão: desligada. |
OTEL_EXPORTER_OTLP_ENDPOINT | Envia logs e traces por OpenTelemetry. |
RATE_LIMIT_GLOBAL, RATE_LIMIT_SENSIVEL | Pedidos por minuto por IP: geral (padrão 240) e nas rotas de login, passkey, POST /api/chat e MCP (padrão 12). |
TRUSTED_PROXIES | Proxies, 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: "voce@suaempresa.com"
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
- Abra a
URLconfigurada. - Entre com o e-mail de
BOOTSTRAP_ADMIN_EMAIL. Ele recebe o link de login (sem SMTP, o link aparece emdocker 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. - 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_KEYentre 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
| Sintoma | Causa provável | O que fazer |
|---|---|---|
| Container não sobe | SECRET_KEY ausente ou curto, ou DATABASE_URL ausente | Defina os dois; a chave com 32+ caracteres. |
password authentication failed | DATABASE_URL errada ou banco inacessível | Confira host, porta e credenciais; de dentro do container use host.docker.internal. |
| O link de login não chega | Sem SMTP | O link está em docker compose logs app. |
| Login volta para a tela inicial atrás de proxy | URL sem https ou proxy sem X-Forwarded-* | Use URL com https e confira TRUSTED_PROXIES. |
| Botão do SSO não aparece | Emissor ou client vazio | Configure em Authentication → Keycloak e veja de onde vem cada valor. |
| Só aparece o botão de SSO, e você não configurou SSO | Variáveis OIDC_* ausentes: entrou o Keycloak de fábrica | Declare OIDC_ISSUER_URL: "" e OIDC_CLIENT_ID: "". |
| A IA não responde | Nenhum provedor ativo | Ative um em Configurações → IA → LLM. |
| Busca semântica vazia | Qdrant desligado ou não indexado | Embeddings__Provider=qdrant e reindexar na tela. |
| Conteúdo sumiu depois de atualizar | /app/data fora de volume | Restaure o backup e use um volume nomeado. |