Tutoriais de VPS
Hospedar o Linkwarden, gerenciador de favoritos auto-hospedado, em uma VPS (2026): Docker Compose, PostgreSQL, MeiliSearch, HTTPS com Caddy e configuração de segurança e backup
Este tutorial explica como implantar o Linkwarden em uma VPS com Docker Compose para criar seu próprio gerenciador de favoritos com arquivamento de páginas web, capturas de tela, busca de texto completo e colaboração em equipe. Abrange PostgreSQL, MeiliSearch, HTTPS com Caddy, NEXTAUTH_SECRET, NEXTAUTH_URL, desativar o cadastro, backup e atualização, isolamento do domínio de conteúdo do usuário e solução de problemas comuns.

Hospedar o Linkwarden, gerenciador de favoritos auto-hospedado, em uma VPS (2026): Docker Compose, PostgreSQL, MeiliSearch, HTTPS com Caddy e configuração de segurança e backup
O problema dos favoritos do navegador não é que você não consiga salvar coisas, mas que, depois de salvas, é difícil encontrá-las de novo.
Você provavelmente já salvou muitos artigos, ferramentas, papers, páginas de produtos, repositórios do GitHub e links de tutoriais. Alguns meses depois, a página pode estar dando 404, o título mudou, o conteúdo está atrás de um login ou você simplesmente não lembra por que a salvou.
O Linkwarden resolve esse problema: não é apenas uma lista de favoritos, mas um sistema de coleção e arquivamento web que você pode auto-hospedar. Ele consegue salvar links, gerar capturas de tela, arquivar o conteúdo das páginas web, organizar por coleções, oferecer busca de texto completo e também funciona bem para pequenas equipes compartilharem uma base de conhecimento.
Este artigo vai implantar o Linkwarden em uma VPS usando Docker Compose, com o Caddy fornecendo HTTPS. Vamos focar em explicar PostgreSQL, MeiliSearch, as variáveis de ambiente principais, desativar o cadastro público, backup e atualização, e os limites de segurança, em vez de copiar as centenas de linhas de configuração do .env.sample oficial.
O Linkwarden atende a estes cenários:
- Você quer auto-hospedar um substituto do Raindrop, Pocket ou Pinboard.
- Você precisa preservar a longo prazo links web, capturas de tela e conteúdo arquivado.
- Você quer que seus favoritos tenham busca de texto completo, não apenas busca por título.
- Você quer manter uma base de conhecimento, uma biblioteca de concorrentes, uma biblioteca de tutoriais ou uma biblioteca de materiais para uma equipe.
- Você não quer todos os seus dados de leitura e favoritos em uma plataforma de terceiros.
Ele é menos indicado para estes cenários:
- Você só quer salvar temporariamente alguns links e os favoritos do navegador bastam.
- Você quer principalmente ler RSS e o FreshRSS é mais adequado.
- Você só precisa ler mais tarde e o Wallabag pode ser mais leve.
- Você não quer manter banco de dados, backup e proxy reverso.
As palavras-chave do Linkwarden são "colecionar + arquivar + buscar". Se você só precisa de um leitor de RSS, ele não é um substituto do FreshRSS; se você precisa preservar evidências de páginas web, links de referência e um ponto de entrada de conhecimento para a equipe, ele será mais adequado.
O Linkwarden é um pouco mais pesado que o FreshRSS, porque por padrão usa PostgreSQL para armazenar dados, MeiliSearch para busca e Playwright / Chromium para gerar capturas de tela e arquivos de páginas web.
| Cenário de uso | Configuração recomendada |
|---|---|
| Uso pessoal leve | 1-2 vCPU / 2GB RAM / 20GB SSD |
| Arquivamento pessoal de longo prazo, muitas capturas | 2 vCPU / 4GB RAM / 40GB SSD |
| Base de conhecimento compartilhada de pequena equipe | 2-4 vCPU / 4GB+ RAM / 80GB+ SSD |
A documentação oficial de instalação menciona que uma VPS com 4GB de RAM roda de forma bem fluida. Com 1GB de memória também pode rodar, mas fica apertado ao tirar capturas de tela e arquivar páginas web. O consumo de recursos do Linkwarden vem principalmente de três partes: o banco de dados, o índice de busca e o arquivamento de páginas web e capturas de tela.
Se sua VPS só tem 1GB de memória livre e já roda bancos de dados, painéis, monitoramento, gateways de IA e outros serviços, não é recomendável espremer o Linkwarden ali também. Ele pode rodar, mas a experiência e a estabilidade não serão boas.
O Docker Compose oficial contém principalmente três tipos de serviços:
| Serviço | Função |
|---|---|
| Linkwarden | Aplicação web, API, tarefas de arquivamento, processamento de capturas de tela |
| PostgreSQL | Armazena dados centrais como usuários, links, coleções e configuração |
| MeiliSearch | Fornece busca de texto completo rápida |
Além disso, este artigo adiciona um contêiner Caddy para obter certificados HTTPS automaticamente e fazer proxy reverso para o Linkwarden.
A imagem oficial do Linkwarden é:
ghcr.io/linkwarden/linkwarden:latest
A porta padrão da aplicação é 3000.
Este artigo assume:
- O sistema da VPS é Ubuntu 22.04 / 24.04 ou Debian 12.
- Docker e Docker Compose V2 estão instalados.
- Você tem um domínio preparado, por exemplo
links.example.com. - O registro A do domínio aponta para o IP público da VPS.
- As portas 80 e 443 podem ser abertas para o Caddy.
Criar os diretórios:
sudo mkdir -p /opt/linkwarden
sudo chown -R "$USER":"$USER" /opt/linkwarden
cd /opt/linkwarden
mkdir -p data pgdata meili_data caddy-data caddy-config backups
Finalidade dos diretórios:
| Diretório | Finalidade |
|---|---|
data | Arquivos de arquivamento do Linkwarden, capturas de tela, PDFs, conteúdo enviado |
pgdata | Arquivos do banco de dados PostgreSQL |
meili_data | Índice de busca do MeiliSearch |
caddy-data | Certificados e dados de execução do Caddy |
caddy-config | Dados de configuração do Caddy |
backups | Diretório de backup local |
Todos esses diretórios precisam entrar no backup. Especialmente pgdata e data: um são dados estruturados e o outro são arquivos de arquivamento, e a restauração fica incompleta sem qualquer um deles.
Criar .env:
nano .env
Escreva o conteúdo abaixo, trocando o domínio e as senhas pelos seus:
LINKWARDEN_DOMAIN=links.example.com
POSTGRES_PASSWORD=replace-with-a-long-random-password
NEXTAUTH_SECRET=replace-with-a-long-random-secret
MEILI_MASTER_KEY=replace-with-a-long-random-meili-key
NEXTAUTH_URL=https://links.example.com/api/v1/auth
BASE_URL=https://links.example.com
NEXT_PUBLIC_DISABLE_REGISTRATION=true
NEXT_PUBLIC_CREDENTIALS_ENABLED=true
Você pode gerar chaves aleatórias com:
openssl rand -base64 32
Há alguns pontos importantes aqui:
NEXTAUTH_SECRETdeve ser um valor aleatório forte, usado para assinar sessões e JWTs.POSTGRES_PASSWORDé a senha do PostgreSQL.MEILI_MASTER_KEYé a chave mestra do MeiliSearch; não a deixe vazia.- Os três valores devem ser strings aleatórias longas, únicas e de alta entropia; não reutilize senhas antigas nem use os placeholders do exemplo.
NEXTAUTH_URLdeve ter o sufixo/api/v1/auth; esse é um ponto muito fácil de errar desde o Linkwarden v2.BASE_URLusa o endereço de acesso público; e-mails e alguns links o utilizam.- Recomenda-se manter
NEXT_PUBLIC_DISABLE_REGISTRATION=trueativado após criar a primeira conta, para evitar o cadastro público aberto.
Se você ainda não tem conta na primeira implantação, pode primeiro definir NEXT_PUBLIC_DISABLE_REGISTRATION=false, criar a conta de administrador e depois voltar para true e executar:
docker compose down
docker compose up -d
Observação: após alterar .env, não execute apenas docker compose restart. A documentação oficial também lembra que alterações em variáveis de ambiente exigem down seguido de up -d para serem recarregadas.
Criar docker-compose.yml:
nano docker-compose.yml
Escreva:
services:
postgres:
image: postgres:16-alpine
container_name: linkwarden-postgres
restart: unless-stopped
env_file:
- .env
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
meilisearch:
image: getmeili/meilisearch:v1.12.8
container_name: linkwarden-meilisearch
restart: unless-stopped
env_file:
- .env
environment:
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
MEILI_NO_ANALYTICS: "true"
volumes:
- ./meili_data:/meili_data
linkwarden:
image: ghcr.io/linkwarden/linkwarden:latest
container_name: linkwarden
restart: unless-stopped
env_file:
- .env
environment:
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
MEILI_HOST: http://meilisearch:7700
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
ports:
- "127.0.0.1:3000:3000"
volumes:
- ./data:/data/data
depends_on:
postgres:
condition: service_healthy
meilisearch:
condition: service_started
caddy:
image: caddy:2-alpine
container_name: linkwarden-caddy
restart: unless-stopped
env_file:
- .env
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy-data:/data
- ./caddy-config:/config
depends_on:
- linkwarden
Aqui a porta 3000 do Linkwarden é vinculada a 127.0.0.1, então não pode ser acessada diretamente pela internet. O que realmente fica exposto externamente é o HTTPS do Caddy.
Se você tem certeza de que apenas o Caddy, dentro do mesmo Compose, precisa acessar o Linkwarden, também pode remover completamente a seção ports, deixando o Linkwarden exposto apenas na rede interna do Docker. Este artigo mantém 127.0.0.1:3000:3000, principalmente por conveniência de depuração local e ao mesmo tempo para evitar o acesso público direto.
PostgreSQL e MeiliSearch não expõem portas para a internet; eles só são acessados pelo Linkwarden dentro da rede do Docker. Não exponha 5432 nem 7700 diretamente para a internet.
Criar Caddyfile:
nano Caddyfile
Escreva:
{$LINKWARDEN_DOMAIN} {
encode zstd gzip
reverse_proxy linkwarden:3000
}
Abrir o firewall:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
Se seu provedor de nuvem tiver grupos de segurança, libere 80 e 443 lá também. O Caddy precisa dessas duas portas para concluir a solicitação de certificado HTTP-01 e o acesso HTTPS.
Se você usa o proxy da Cloudflare, o modo SSL/TLS deve ser Full (strict), não Flexible. O Flexible costuma causar problemas com o protocolo de origem, redirecionamentos e callbacks de login.
Execute:
cd /opt/linkwarden
docker compose up -d
Ver os contêineres:
docker compose ps
Ver os logs:
docker compose logs -f --tail=100 linkwarden
Abra o navegador e acesse:
https://links.example.com
Crie uma conta após entrar pela primeira vez. Depois de criada, confirme que .env contém:
NEXT_PUBLIC_DISABLE_REGISTRATION=true
Depois recrie os contêineres para que as variáveis de ambiente tenham efeito:
docker compose down
docker compose up -d
Assim, usuários públicos não poderão cadastrar contas livremente.
O MeiliSearch é um mecanismo de busca e, se você não definir MEILI_MASTER_KEY, o controle de acesso ficará fraco. Embora este artigo não exponha 7700 para a internet, serviços internos também devem ter uma chave.
A forma correta:
MEILI_MASTER_KEY=replace-with-a-long-random-meili-key
E configurar ambas no serviço do Linkwarden:
MEILI_HOST: http://meilisearch:7700
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
Não remova o MeiliSearch por conveniência. A busca avançada do Linkwarden depende dele, e a diferença fica bem clara quando o número de favoritos aumenta.
O Linkwarden salva snapshots de páginas web, arquivos HTML, capturas de tela e PDFs. As páginas web arquivadas são essencialmente "conteúdo não confiável", que pode conter scripts estranhos, HTML malicioso ou páginas enganosas.
O projeto oficial oferece o recurso User Content Domain, que permite colocar o conteúdo arquivado em outro domínio, por exemplo:
links.example.com
usercontent.example.com
Assim, a aplicação principal e o conteúdo arquivado não ficam na mesma origem, e o limite de segurança fica mais claro. O ponto-chave aqui é "hostname separado / origem separada"; não continue reutilizando o domínio da aplicação principal.
Para uso pessoal leve, você pode não configurar isso por enquanto, mas se for salvar muitas páginas web desconhecidas e os membros da equipe forem abrir o conteúdo arquivado uns dos outros, é aconselhável planejar um domínio de conteúdo do usuário o quanto antes.
A ideia geral é:
- O domínio principal
links.example.comacessa a aplicação Linkwarden. - O domínio de conteúdo
usercontent.example.comé usado apenas para acessar o conteúdo arquivado. - Os dois domínios podem apontar para a mesma VPS.
- O Caddy ou o Nginx diferenciam o encaminhamento e as regras de acesso por host.
- Definir
NEXT_PUBLIC_USER_CONTENT_DOMAINem.env.
Essa é uma configuração de segurança avançada. Este artigo primeiro conclui a implantação da aplicação principal, mas você deve pelo menos saber que os arquivos de páginas web não são recursos estáticos totalmente confiáveis.
O backup do Linkwarden não pode se limitar a um único diretório. No mínimo, deve incluir:
- O banco de dados PostgreSQL.
- O diretório de arquivos de arquivamento
data. - O diretório do índice do MeiliSearch.
.env,docker-compose.yml,Caddyfile.
Criar um script de backup:
nano backup-linkwarden.sh
Escreva:
#!/usr/bin/env bash
set -euo pipefail
APP_DIR="/opt/linkwarden"
BACKUP_DIR="$APP_DIR/backups"
DATE="$(date +%F-%H%M)"
mkdir -p "$BACKUP_DIR/$DATE"
cd "$APP_DIR"
# 先停止会写入数据库和归档文件的服务,避免 SQL 快照和 data 目录时间点不一致。
docker compose stop linkwarden meilisearch
# 备份 PostgreSQL 逻辑数据。PostgreSQL 容器保持运行即可执行 pg_dump。
docker compose exec -T postgres pg_dump -U postgres postgres > "$BACKUP_DIR/$DATE/postgres.sql"
# 备份归档文件、搜索索引和配置文件。
tar czf "$BACKUP_DIR/$DATE/linkwarden-files.tar.gz" data meili_data docker-compose.yml Caddyfile .env
# 重新启动服务。
docker compose start meilisearch linkwarden
# 保留最近 14 天备份。
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +14 -exec rm -rf {} \;
echo "Backup saved to $BACKUP_DIR/$DATE"
Essa ordem é importante: pare o Linkwarden primeiro e depois faça o pg_dump e o arquivamento de arquivos. Caso contrário, o banco de dados pode já registrar um arquivo de arquivamento, mas o arquivo ainda não terminou de ser gravado quando você empacota o diretório data, ou o contrário acontece e os arquivos não batem com o banco de dados.
Adicionar permissões de execução e testar:
chmod +x backup-linkwarden.sh
./backup-linkwarden.sh
ls -lh backups
Tarefa agendada:
crontab -e
Backup todos os dias às 3 da manhã:
0 3 * * * /opt/linkwarden/backup-linkwarden.sh >> /opt/linkwarden/backups/backup.log 2>&1
O backup local é apenas a primeira camada. Recomenda-se usar também o rclone para sincronizar com armazenamento de objetos, um NAS ou outra VPS.
Se você mudar o conteúdo arquivado para S3 / MinIO / armazenamento de objetos, o foco do backup muda: o banco de dados continua precisando de backup, e o bucket de armazenamento de objetos também precisa de versionamento ou uma estratégia de snapshots.
Pare os serviços antes de restaurar:
cd /opt/linkwarden
docker compose down
Primeiro restaure o backup em nível de arquivos. Isso restaura os arquivos de arquivamento data, o índice de busca meili_data, .env, docker-compose.yml e Caddyfile:
tar xzf /opt/linkwarden/backups/2026-07-09-0300/linkwarden-files.tar.gz -C /opt/linkwarden
Restaurar apenas postgres.sql não é suficiente, porque capturas de tela, PDFs, arquivos HTML e arquivos enviados ficam no diretório data. O backup de arquivos e o backup do banco de dados devem ser restaurados juntos para que o Linkwarden fique em um estado completo após a restauração.
Inicie primeiro o PostgreSQL:
docker compose up -d postgres
Aguarde o banco de dados ficar saudável e restaure o SQL:
docker compose exec -T postgres psql -U postgres postgres < /opt/linkwarden/backups/2026-07-09-0300/postgres.sql
Depois inicie todos os serviços:
docker compose up -d
Verifique após restaurar:
- Você consegue fazer login?
- As coleções e os links existem?
- As capturas de tela, PDFs e arquivos HTML abrem?
- A busca funciona?
- O certificado do Caddy está normal?
Se o índice do MeiliSearch tiver problemas, normalmente pode ser reconstruído; mas se os diretórios do PostgreSQL e data forem perdidos, os dados centrais e os arquivos de arquivamento serão afetados.
Faça backup antes de atualizar:
cd /opt/linkwarden
./backup-linkwarden.sh
Atualizar:
docker compose pull
docker compose down
docker compose up -d
Por que não apenas restart? Porque quando variáveis de ambiente, imagens e definições de serviço mudam, restart não recarrega a configuração por completo.
Verifique após atualizar:
docker compose ps
docker compose logs --tail=100 linkwarden
Se você está atualizando de uma versão muito antiga do Linkwarden, leia primeiro as notas oficiais de atualização. Especialmente para a migração de v1 para v2, NEXTAUTH_URL deve ter /api/v1/auth, e etapas adicionais de migração podem ser necessárias.
Verifique primeiro:
NEXTAUTH_URL=https://links.example.com/api/v1/auth
BASE_URL=https://links.example.com
NEXTAUTH_URL não deve ser apenas o domínio raiz; deve ter /api/v1/auth. Esse é um dos problemas de autenticação mais comuns.
Após alterar .env, execute:
docker compose down
docker compose up -d
Confirme .env:
NEXT_PUBLIC_DISABLE_REGISTRATION=true
Depois recrie os contêineres:
docker compose down
docker compose up -d
Apenas reiniciar pode não carregar as novas variáveis de ambiente.
Verifique o MeiliSearch:
docker compose logs --tail=100 meilisearch
Confirme que .env corresponde a MEILI_MASTER_KEY no Compose, e que MEILI_HOST no Linkwarden é:
http://meilisearch:7700
Não exponha a porta do MeiliSearch para a internet.
O Linkwarden usa um ambiente de navegador para processar páginas web. As causas de falha podem ser:
- A VPS tem pouca memória.
- A página web de destino tem anti-scraping, login obrigatório ou restrições regionais.
- Anomalias de acesso por DNS ou IPv6.
- O certificado TLS do site de destino tem problemas.
- O contêiner não tem tempo suficiente para concluir a captura de tela.
Veja primeiro os logs:
docker compose logs --tail=200 linkwarden
Se muitas tarefas de arquivamento falharem, primeiro aumente a VPS para 2GB ou 4GB de memória e depois investigue a rede e as restrições do site de destino.
Verifique:
- Se o domínio resolve para a VPS.
- Se 80/443 estão liberados no firewall do sistema.
- Se o grupo de segurança da nuvem os libera.
- Se já há Nginx/Apache ocupando 80/443 no servidor.
- Se a Cloudflare está usando o modo SSL/TLS errado.
Ver os logs do Caddy:
docker compose logs --tail=100 caddy
Antes de entrar em produção, no mínimo faça isto:
- Não expor a porta 5432 do PostgreSQL.
- Não expor a porta 7700 do MeiliSearch.
- Vincular o Linkwarden apenas a
127.0.0.1:3000, ou não publicar nenhuma porta do host e deixar apenas o Caddy acessar pela rede do Docker. - Usar Caddy ou Nginx para fornecer HTTPS.
- Usar valores aleatórios fortes para
NEXTAUTH_SECRET,POSTGRES_PASSWORDeMEILI_MASTER_KEY. - Desativar o cadastro público após criar a primeira conta.
- Não enviar
.envpara o GitHub. - Fazer backup periódico do PostgreSQL e dos arquivos de arquivamento.
- Se for usado por uma equipe, considere configurar um domínio de conteúdo do usuário para isolar o HTML arquivado.
- Não ativar de forma descuidada opções que acessem redes privadas ou ignorem a verificação TLS.
O Linkwarden acessa ativamente os links de páginas web que você salva, então, assim como o RSSHub e o FreshRSS, você deve prestar atenção aos limites das requisições do lado do servidor. Por padrão, não deixe que ele acesse endereços de rede interna nem o trate como um proxy de scraping de páginas web arbitrárias.
| Ferramenta | Para que é mais adequada |
|---|---|
| Linkwarden | Colecionar links, arquivar páginas web, base de conhecimento de equipe, busca de texto completo |
| FreshRSS | Assinaturas RSS, fluxos de leitura, sincronização de lido |
| Wallabag | Ler mais tarde, salvar o corpo do artigo |
| Nextcloud Bookmarks | Sincronização simples de favoritos, adequada para usuários do Nextcloud |
Se você lê feeds todos os dias, escolha o FreshRSS. Se você quer preservar "material de páginas web que vai usar depois", escolha o Linkwarden. Se você só quer ler artigos longos mais tarde, o Wallabag é mais leve.
Verifique antes do uso formal:
- O domínio resolve para a VPS.
- 80/443 estão liberados no firewall e no grupo de segurança.
-
NEXTAUTH_URLestá definido comohttps://links.example.com/api/v1/auth. -
BASE_URLestá definido com o domínio HTTPS público. -
NEXTAUTH_SECRETé um valor aleatório forte. -
POSTGRES_PASSWORDé um valor aleatório forte. -
MEILI_MASTER_KEYé um valor aleatório forte. - O PostgreSQL não está exposto à internet.
- O MeiliSearch não está exposto à internet.
- O Linkwarden está vinculado apenas a
127.0.0.1:3000local, ou nenhuma porta do host é publicada. - O cadastro está desativado após criar a conta.
- O script de backup foi testado.
- Pelo menos um simulado de restauração foi feito.
- O isolamento do domínio de conteúdo do usuário foi avaliado para uso em equipe.
O Linkwarden é muito adequado para rodar a longo prazo em uma VPS. Ele é mais confiável que os favoritos do navegador, mais adequado para arquivar que ferramentas simples de favoritos e mais adequado como base de conhecimento que ferramentas puramente de ler mais tarde.
Na implantação, o mais importante não é apenas colocar o serviço para rodar, mas acertar alguns pontos-chave: não expor o PostgreSQL à internet, definir a chave mestra do MeiliSearch, fazer NEXTAUTH_URL incluir /api/v1/auth, desativar o cadastro após criar a conta e fazer backup periódico do banco de dados e dos arquivos de arquivamento.
Se você já tem uma VPS com 2GB ou 4GB de memória, o Linkwarden é um gerenciador de favoritos que vale muito a pena auto-hospedar. Ele consegue transformar os links de "ler mais tarde" em uma base de conhecimento realmente pesquisável, arquivável e preservável a longo prazo.