DevOps и Docker 11 мин чтения 2026-10-04

Менеджер закладок и веб-архив Linkwarden в Docker Compose: вечное сохранение статей, снимков страниц и PDF на VDS

Полезные ссылки умирают, а облачные сервисы закрываются или требуют подписку? Разбираем установку Linkwarden на VDS в Docker Compose: автоматическое сохранение копий веб-страниц, снимков и PDF, подключение Nginx Reverse Proxy и расширений для браузера.

Инженерная редакция SysKit Проверено инженерами SysKit.ru

Каждый инженер, сисадмин или разработчик со временем накапливает сотни закладок: статьи с решением редких багов, официальные RFC, архитектурные мануалы, репозитории GitHub и фрагменты документации. Однако спустя год-два при попытке открыть сохраненный материал пользователь все чаще видит ошибки 404 Not Found, 410 Gone или страницу блокировки с требованием платной подписки.

В цифровой среде это явление называется Link Rot («вымирание ссылок»). По данным масштабного исследования Pew Research Center:

  • Около 38% веб-страниц, существовавших в 2013 году, к сегодняшнему дню полностью недоступны.
  • Даже среди относительно свежих материалов (опубликованных за последние 2–3 года) свыше 8–10% ссылок ломаются из-за редизайна сайтов, закрытия блогов или смены структуры URL.
  • Коммерческие облачные сервисы (закрытие Omnivore, деградация Pocket, платная подписка на полнотекстовый поиск в Raindrop) не гарантируют вечной сохранности данных.

⚠️ Чем стандартная закладка отличается от архивации

Классическая закладка в браузере хранит лишь текстовую строку URL. Если домен истек, владелец удалил статью или сайт попал под блокировку, закладка превращается в «мертвый груз». Настоящая веб-архивация требует создания локального слепка страницы в момент сохранения: очищенного текста (Readability), точного скриншота оформления и независимого PDF-документа.

Именно эту задачу решает Linkwarden — современный open-source сервис управления закладками и совместной архивации с веб-интерфейсом, расширениями для всех популярных браузеров и автоматическим сохранением копий страниц на вашем собственном VDS.

2. Архитектура Linkwarden и системные требования к VDS

Linkwarden спроектирован по модульной микросервисной архитектуре и ориентирован на автономную работу в изолированных контейнерах:

  • Основное приложение (Next.js / Node.js): обеспечивает работу отзывчивого пользовательского веб-интерфейса, REST API для браузерных расширений, авторизацию пользователей и систему тегов.
  • База данных (PostgreSQL 16): хранит метаданные ссылок, иерархию коллекций, теги, права доступа и учетные записи.
  • Движок архивации (Headless Chromium / Playwright): фоновый воркер, который при добавлении ссылки в фоновом режиме загружает страницу в скрытом браузере, прокручивает её для подгрузки динамического контента и сохраняет результат в трех форматах:
    • Readability (HTML): очищенный от рекламы и мусорных скриптов текст для комфортного чтения.
    • Screenshot (PNG/WebP): полноразмерный снимок экрана страницы в оригинальном дизайне.
    • PDF: векторный документ, пригодный для печати, аннотаций и поиска.
Параметр Минимальные требования Рекомендуемые параметры
Процессор (vCPU) 1 ядро (2.4+ ГГц) 2 ядра (для быстрого рендеринга Playwright)
Оперативная память (RAM) 1 ГБ + Swap (1–2 ГБ) 2 ГБ RAM
Дисковое пространство 15 ГБ NVMe 30+ ГБ NVMe (PDF и скриншоты со временем растут)
Операционная система Ubuntu 22.04 LTS Ubuntu 24.04 LTS / Debian 12

💡 Почему для Linkwarden критичен быстрый NVMe и Swap

В момент рендеринга тяжелых веб-страниц (например, дашбордов на React или длинных лонгридов с сотнями изображений) встроенный браузер Chromium кратковременно потребляет от 400 до 800 МБ RAM. Если на сервере всего 1 ГБ оперативной памяти, обязательно включите zRAM или Swap-файл на 2 ГБ, чтобы системный механизм OOM Killer не завершал фоновые процессы.

3. Развертывание стека Linkwarden и PostgreSQL в Docker Compose

Развертывание сервиса выполняется на выделенном каталоге сервера с изоляцией данных и обязательным ограничением потребления ресурсов.

Создадим рабочую директорию проекта и перейдем в нее:

# Создаем изолированную директорию для стека
sudo mkdir -p /opt/linkwarden
cd /opt/linkwarden

Сгенерируем надежные криптографические ключи для сессий NextAuth и пароля базы данных PostgreSQL:

# Генерация 64-символьного секрета NextAuth
openssl rand -hex 32

# Генерация пароля для PostgreSQL
openssl rand -hex 16

Создадим файл переменных окружения /opt/linkwarden/.env:

# URL вашего сервиса с обязательным суффиксом /api/v1/auth
NEXTAUTH_URL=https://linkwarden.example.com/api/v1/auth

# Секрет, сгенерированный через openssl rand -hex 32
NEXTAUTH_SECRET=9f8c7b6a5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a

# Пароль суперпользователя базы данных PostgreSQL
POSTGRES_PASSWORD= супер_стойкий_пароль_бд_из_openssl

# Отключение открытой публичной регистрации после создания первой учетной записи
NEXT_PUBLIC_DISABLE_REGISTRATION=false

# Локальный каталог хранения сгенерированных PDF и скриншотов внутри контейнера
STORAGE_FOLDER=/data/data

⚠️ Критически важный суффикс в NEXTAUTH_URL

Обратите особое внимание: переменная NEXTAUTH_URL обязана оканчиваться на /api/v1/auth (например, https://linkwarden.example.com/api/v1/auth). Если указать только доменное имя без этого суффикса, внутренняя библиотека аутентификации NextAuth не сможет сопоставить эндпоинты авторизации, и при попытке входа возникнет циклическая ошибка «Client-side exception has occurred».

Создадим манифест docker-compose.yml:

services:
  postgres:
    image: postgres:16-alpine
    container_name: linkwarden-postgres
    restart: unless-stopped
    env_file: .env
    environment:
      POSTGRES_USER: postgres
      POSTGRES_DB: postgres
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    mem_limit: 512m
    cpus: 0.5
    networks:
      - linkwarden-net

  linkwarden:
    image: ghcr.io/linkwarden/linkwarden:v2.8.3
    container_name: linkwarden-app
    restart: unless-stopped
    env_file: .env
    environment:
      - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
    ports:
      # Привязываем порт 3000 строго к локальному интерфейсу 127.0.0.1 для защиты от обхода UFW
      - "127.0.0.1:3000:3000"
    volumes:
      - ./data:/data/data
    mem_limit: 1536m
    cpus: 1.5
    depends_on:
      - postgres
    networks:
      - linkwarden-net

networks:
  linkwarden-net:
    driver: bridge

💡 Сетевая безопасность и защита от обхода фаервола

Обратите внимание на директиву "127.0.0.1:3000:3000": публикация порта только на локальном интерфейсе предотвращает автоматический проброс порта наружу через цепочки Docker в iptables в обход системного файрвола UFW. Порт СУБД 5432 вовсе не публикуется наружу, оставаясь доступным только внутри изолированной сети linkwarden-net.

Запустим контейнеры в фоновом режиме:

# Запуск стека сервисов
sudo docker compose up -d

# Проверка статуса запущенных контейнеров
sudo docker compose ps

4. Настройка Nginx Reverse Proxy и автоматического выпуска SSL

Для безопасной работы браузерных расширений и шифрования передаваемых учетных данных настроим фронтальный обратный прокси Nginx с поддержкой протокола WebSocket и бесплатным сертификатом Let's Encrypt.

Создадим конфигурационный файл виртуального хоста /etc/nginx/sites-available/linkwarden.conf:

server {
    listen 80;
    server_name linkwarden.example.com;

    # Увеличиваем лимит тела запроса для импорта больших баз закладок из HTML/JSON
    client_max_body_size 64M;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;

        # Проброс заголовков для поддержки постоянных WebSocket-соединений
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Передача реального IP-адреса клиента в бэкенд Linkwarden
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Таймауты ожидания ответов при тяжелом рендеринге страниц
        proxy_read_timeout 120s;
        proxy_send_timeout 120s;
    }
}

Активируем виртуальный хост и проверим корректность синтаксиса:

# Создаем символическую ссылку в sites-enabled
sudo ln -s /etc/nginx/sites-available/linkwarden.conf /etc/nginx/sites-enabled/

# Тестируем конфигурацию Nginx
sudo nginx -t

# Применяем настройки без остановки сервера
sudo systemctl reload nginx

Выпустим доверенный SSL-сертификат с автоматической настройкой перенаправления с HTTP на HTTPS:

# Получение сертификата Let's Encrypt через Certbot с автоматическим редиректом
sudo certbot --nginx -d linkwarden.example.com --redirect

Убедитесь, что SSL-сертификат активен и корректно отдает цепочку доверия с помощью онлайн-утилиты Проверка SSL-сертификата онлайн, а также проверьте доступность портов через Сканер портов онлайн.

5. Подключение расширений браузера и организация базы знаний

После успешного развертывания откройте адрес https://linkwarden.example.com в браузере и создайте учетную запись администратора.

Для удобного сохранения статей в один клик подключим официальные браузерные расширения:

  1. Установите расширение Linkwarden из интернет-магазина Chrome Web Store или Firefox Add-ons.
  2. В веб-интерфейсе Linkwarden перейдите в Settings → Access Tokens.
  3. Нажмите Create Token, задайте имя (например, Work-Chrome) и скопируйте сгенерированный секретный ключ.
  4. Откройте настройки расширения в браузере:
    • В поле Instance URL укажите: https://linkwarden.example.com
    • В поле Access Token вставьте скопированный ключ и нажмите Save.

💡 Быстрая категоризация через Коллекции и Теги

Разделите базу знаний на логические коллекции: DevOps & Linux, Backend Архитектура, Информационная безопасность. При сохранении страницы расширение позволяет выбрать целевую папку, добавить теги (например, #docker, #nginx, #troubleshooting) и сразу инициировать генерацию PDF и скриншота.

Если у вас уже есть база закладок в Google Chrome, Firefox, Pocket или Raindrop, экспортируйте ее в стандартный файл bookmarks.html и загрузите в Linkwarden через меню Settings → Import. Сервис автоматически распарсит папки и постепенно сформирует офлайн-архивы для каждой ссылки.

6. Автоматическое резервное копирование базы и архивов

Архив знаний представляет огромную ценность, поэтому регулярное резервное копирование — обязательный этап обслуживания сервиса.

Создадим скрипт автоматического снятия дампа базы данных и упаковки сохраненных медиафайлов /opt/linkwarden/backup.sh:

#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="/var/backups/linkwarden"
DATE=$(date +%Y-%m-%d_%H%M%S)
mkdir -p "$BACKUP_DIR"

echo "=== Запуск резервного копирования Linkwarden [$DATE] ==="

# 1. Дамп базы данных PostgreSQL без открытых паролей в аргументах
cd /opt/linkwarden
sudo docker compose exec -T postgres pg_dump -U postgres postgres | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"

# 2. Создание архива директории с медиафайлами (PDF, скриншоты)
tar -czf "$BACKUP_DIR/data_$DATE.tar.gz" -C /opt/linkwarden data

# 3. Удаление локальных копий старше 14 дней
find "$BACKUP_DIR" -type f -name "*.gz" -mtime +14 -delete

echo "=== Резервное копирование успешно завершено ==="

Сделайте скрипт исполняемым и добавьте его в системный планировщик cron:

# Назначаем права на выполнение только владельцу root
sudo chmod 700 /opt/linkwarden/backup.sh

# Добавляем ежедневное выполнение в 03:30 ночи
(sudo crontab -l 2>/dev/null; echo "30 3 * * * /opt/linkwarden/backup.sh >> /var/log/linkwarden-backup.log 2>&1") | sudo crontab -

Для отправки сформированных архивов в независимое облачное хранилище изучите наше руководство Автоматический бэкап баз данных и файлов с VDS в S3-хранилище с уведомлениями в Telegram.

Разверните собственную независимую базу знаний на быстром VDS

Для стабильной работы Linkwarden, быстрой обработки веб-страниц встроенным headless-браузером и надежного хранения сотен PDF-документов выбирайте производительные виртуальные серверы с быстрыми NVMe-накопителями и широким каналом.

Рекомендуемые VDS-провайдеры

Отказоустойчивые сервера с быстрыми NVMe и каналом до 1 Гбит/с

Выбор редакции
⚡

Timeweb Cloud — Быстрые NVMe VDS

Надежные облачные серверы с быстрыми NVMe-дисками, выделенным статическим IP и открытыми портами 80/443 для безотказной работы SSL и Nginx.

Конфигурация
1 vCPU 3.3 ГГц • 1 ГБ RAM • 15 ГБ NVMe • Трафик без лимита
Премиум Tier III
🔷

Selectel — Премиальные облачные серверы

Инфраструктура корпоративного уровня в дата-центрах Tier III: почасовая тарификация, приватные VLAN и прямое подключение к ведущим точкам обмена трафиком.

Конфигурация
1 vCPU • 2 ГБ RAM • 30 ГБ NVMe • Канал 100 Мбит/с
Для вебмастеров
🟠

Beget — Простота и стабильность VDS

Удобная интуитивная панель управления, автоматические ежедневные бэкапы и мгновенное развертывание LEMP-стека с предустановленным Nginx.

Конфигурация
1 vCPU 3.0 ГГц • 1 ГБ RAM • 20 ГБ NVMe • Трафик без лимита

Инструменты SysKit по теме статьи

Бесплатные утилиты для проверки и диагностики вашего сервера

Вопросы и ответы (FAQ)

Популярные вопросы вебмастеров и сисадминов по данной теме

Как сильно Linkwarden нагружает процессор и оперативную память при импорте тысяч закладок?
При разовом импорте большого файла закладок встроенный воркер Playwright обрабатывает ссылки последовательно небольшими пакетами (параметр ARCHIVE_TAKE_COUNT). Пиковое потребление памяти браузером составляет около 400–700 МБ RAM. Благодаря лимиту mem_limit: 1536m контейнер не превысит безопасный порог и не повлияет на работу соседних служб на VDS.
Можно ли отключить публичную регистрацию после создания первой учетной записи?
Да, это настоятельно рекомендуется для безопасности. В файле /opt/linkwarden/.env измените значение переменной NEXT_PUBLIC_DISABLE_REGISTRATION=true и перезапустите контейнеры командой sudo docker compose up -d. Форма открытой регистрации на странице входа исчезнет, а новые аккаунты сможет создавать только администратор через панель управления.
Что делать, если некоторые сайты блокируют сохранение архивов из-за Cloudflare капчи?
Сайты с агрессивной защитой Cloudflare Bot Fight Mode могут отдавать капчу при посещении сервером с IP-адресом дата-центра. В таком случае в расширении Linkwarden сохранятся метаданные страницы, а снимок можно запустить повторно из интерфейса или использовать встроенный режим очищенного чтения Readability, который парсит исходный DOM-документ.
Как подключить внешнее S3-совместимое объектное хранилище для хранения PDF и скриншотов?
Linkwarden нативно поддерживает S3-хранилища (AWS S3, MinIO, Selectel, Timeweb S3). Для этого в файле .env достаточно заполнить параметры SPACES_KEY, SPACES_SECRET, SPACES_ENDPOINT, SPACES_BUCKET_NAME и установить SPACES_FORCE_PATH_STYLE=true. Все сгенерированные файлы будут автоматически выгружаться в облачный бакет, освобождая локальный диск сервера.

Читайте также в блоге SysKit

Смежные руководства и полезные технические статьи