MAATRIX / Блог / Joplin Server в Docker Compose: готовый файл

Joplin Server в Docker Compose: готовый файл

MAATRIX

Joplin — отличный редактор заметок, но синхронизация через Joplin Cloud стоит подписку, а Dropbox или OneDrive в качестве бэкенда для синхронизации работают медленно и не дают контроля над данными. Joplin Server решает это: вы поднимаете свой сервер синхронизации на VPS, подключаете к нему desktop и мобильные клиенты, и заметки живут только у вас — без чужого облака и без ежемесячной платы за синхронизацию. Ниже — рабочий docker-compose.yml с Postgres, разбор переменных окружения и подключение HTTPS через Caddy.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что нужно для запуска

Joplin Server — это Node.js-приложение плюс база данных. Официально поддерживаются PostgreSQL (рекомендуется для продакшена) и SQLite (годится для теста, но при синхронизации нескольких устройств лучше сразу брать Postgres — меньше шансов упереться в блокировки базы при параллельных запросах от клиентов).

Что понадобится:

  • VPS с Docker и Docker Compose (плагин docker compose, не устаревший docker-compose).
  • Домен или поддомен, направленный на IP сервера — Joplin Server жёстко завязан на APP_BASE_URL, и без корректного домена синхронизация будет обрываться.
  • Открытые порты 80/443 для HTTPS.
  • Немного диска под вложения — заметки с картами хранятся вместе с базой (см. раздел про storage driver ниже).

По ресурсам ориентируйтесь на минимальный VPS: Joplin Server сам по себе лёгкий, основная нагрузка — это Postgres и объём хранимых вложений. Для 1–5 пользователей с обычными текстовыми заметками достаточно скромной конфигурации, но если вы храните в заметках много фото и PDF — берите диск с запасом, это первое, что закончится.

Docker Compose: полный файл

Структура: контейнер db с Postgres, контейнер app с Joplin Server, общий volume для данных Postgres и (опционально) volume для файлового хранилища вложений.

version: "3.8"

services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: joplin
      POSTGRES_PASSWORD: change_me_strong_password
      POSTGRES_DB: joplin
    volumes:
      - joplin_db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U joplin"]
      interval: 10s
      timeout: 5s
      retries: 5

  app:
    image: joplin/server:latest
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "127.0.0.1:22300:22300"
    environment:
      APP_PORT: "22300"
      APP_BASE_URL: "https://notes.example.com"
      DB_CLIENT: "pg"
      POSTGRES_HOST: "db"
      POSTGRES_PORT: "5432"
      POSTGRES_DATABASE: "joplin"
      POSTGRES_USER: "joplin"
      POSTGRES_PASSWORD: "change_me_strong_password"
      MAILER_ENABLED: "false"
      SIGNUP_ENABLED: "false"
      STORAGE_DRIVER: "Type=Database"
      COOKIE_SECURE: "true"

volumes:
  joplin_db:

Замените notes.example.com на свой домен и придумайте нормальный пароль вместо change_me_strong_password — этот пароль указан в двух местах, следите, чтобы они совпадали. Порт 22300 намеренно опубликован только на 127.0.0.1 — наружу сервис отдаёт reverse proxy, о нём ниже.

Запуск:

docker compose up -d
docker compose logs -f app

При первом старте Joplin Server сам прогонит миграции базы — в логах должны появиться строки о применённых migrations и в конце сообщение, что сервер слушает порт 22300.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Переменные окружения: что менять обязательно

Часть переменных можно оставить по умолчанию, но несколько штук напрямую влияют на то, заработает ли синхронизация вообще.

ПеременнаяЗа что отвечаетЧто учесть
APP_BASE_URLПубличный URL сервераДолжен точно совпадать с адресом, который видят клиенты, включая https:// и без слэша в конце
DB_CLIENTДрайвер базыpg для Postgres, sqlite3 для SQLite — для прод-сервера берите pg
MAILER_ENABLEDОтправка email (сброс пароля, приглашения)Если false, восстановить пароль пользователя через форму не получится — это ручной процесс через администратора
SIGNUP_ENABLEDПубличная самостоятельная регистрацияДля личного сервера почти всегда держите false и создавайте пользователей вручную из админки
STORAGE_DRIVERГде хранятся тела заметок и вложенийType=Database — всё в Postgres, проще для бэкапа; Type=Filesystem, Path=/data — вложения на диске, база компактнее
COOKIE_SECUREТребовать HTTPS для cookie сессииДержите true, если сервер уже за HTTPS-прокси (что и должно быть)
MAX_TIME_DRIFTДопустимая рассинхронизация времени клиента и сервераОбычно трогать не нужно, если время на сервере и клиентах синхронизировано через NTP

Если планируете рассылку email (приглашения пользователям, сброс пароля), добавьте блок MAILER_HOST, MAILER_PORT, MAILER_SECURITY, MAILER_AUTH_USER, MAILER_AUTH_PASSWORD, MAILER_NOREPLY_EMAIL со своим SMTP — например, тем же почтовым сервисом, что и для остальных проектов на сервере.

Про STORAGE_DRIVER: для личного использования проще оставить Type=Database — один volume, один процесс бэкапа. Если заметок с большими вложениями много и база начинает разрастаться, переход на Type=Filesystem разгружает Postgres, но тогда бэкапить нужно уже два места — базу и папку с файлами.

Reverse proxy и HTTPS через Caddy

Joplin Server сам HTTPS не отдаёт — это задача прокси. Проще всего поднять Caddy рядом (в том же docker-compose или отдельным контейнером), он сам получит сертификат Let's Encrypt по домену.

Caddyfile:

notes.example.com {
    reverse_proxy 127.0.0.1:22300
}

Или добавьте Caddy как ещё один сервис в тот же compose-файл, если хотите держать всё в одном месте:

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
    depends_on:
      - app

и добавьте caddy_data в общий блок volumes. Если у вас на сервере уже стоит Traefik или nginx для других сайтов — используйте их и просто добавьте виртуальный хост на порт 22300, отдельный Caddy тогда не нужен. Разницу между вариантами разбирали в статье Caddy или Nginx — что выбрать для сервера.

Главная ошибка на этом шаге — APP_BASE_URL не совпадает с реальным адресом (например, забыли протокол или указали IP вместо домена). Клиент в таком случае либо не подключается, либо синхронизация зависает на первом же запросе — проверяйте это в первую очередь.

Первый запуск, админ и подключение клиентов

После docker compose up -d откройте https://notes.example.com в браузере — должна появиться форма входа. Стандартный админ-аккаунт после установки: логин admin@localhost, пароль admin. Первым делом зайдите под ним и смените и логин, и пароль — дефолтные значения известны всем, кто читал документацию Joplin Server.

Дальше в админке (/admin/users) создайте обычных пользователей — по одному на каждого человека, который будет синхронизировать заметки. У каждого пользователя своё пространство, они не видят чужие заметки.

Подключение клиента (desktop-приложение Joplin):

  1. Настройки → Синхронизация.
  2. Target sync: Joplin Server.
  3. URL сервера: ваш https://notes.example.com.
  4. Email и пароль — те, что создали в админке.
  5. Нажать «Синхронизировать сейчас».

Для мобильного приложения (Android/iOS) — те же поля в разделе настроек синхронизации. Если у вас уже были заметки локально или в другом облаке, при первой синхронизации Joplin аккуратно сольёт их с сервером — конфликтующие версии он не затирает молча, а создаёт конфликт-копии, которые видно в специальной папке.

Бэкап, обновление и мониторинг

Бэкап при STORAGE_DRIVER=Type=Database сводится к бэкапу одного Postgres:

docker compose exec db pg_dump -U joplin joplin > joplin_backup_$(date +%F).sql

Автоматизировать это стоит тем же способом, что и для других баз на сервере — cron-задачей с ротацией старых копий и, желательно, выгрузкой копии за пределы сервера. Общий подход к автоматизации бэкапов и переносу вне сервера разбирали в статье про BorgBackup в Docker Compose — тот же принцип применим и к дампу Postgres от Joplin.

Восстановление:

cat joplin_backup_2026-08-20.sql | docker compose exec -T db psql -U joplin joplin

Обновление до новой версии — стандартно для docker compose:

docker compose pull app
docker compose up -d app

При обновлении Joplin Server сам применит новые миграции базы при старте — это видно в логах. Перед обновлением на прод-сервере сделайте свежий дамп базы, миграции иногда необратимы без бэкапа. Для продакшен-сервера разумнее зафиксировать конкретный тег образа вместо latest, чтобы обновление происходило осознанно, а не при случайном docker compose pull — тот же принцип описан в статье Docker Compose для продакшена: частые ошибки и решения.

Из мониторинга минимум — проверять, что контейнер app жив (docker compose ps) и что диск под Postgres не заполняется под вложениями. Для базового алертинга по доступности домена достаточно внешнего HTTP-чекера, поднимать что-то тяжёлое ради одного сервиса синхронизации заметок избыточно.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли обойтись без Postgres и хранить всё в SQLite?

Технически да — установите DB_CLIENT=sqlite3, и Joplin Server создаст файл базы сам. Для одного пользователя с редкими синхронизациями это работает, но при нескольких активных клиентах SQLite чаще упирается в блокировки при параллельных запросах — Postgres надёжнее с самого начала.

Синхронизация зависает или клиент пишет ошибку соединения — с чего начать разбор?

В первую очередь сверьте APP_BASE_URL в контейнере с реальным адресом, по которому клиент стучится (протокол, домен, без завершающего слэша). Второе по частоте — сертификат HTTPS не выпустился (проверьте логи Caddy/Traefik) или порт 22300 случайно торчит наружу вместо того, чтобы идти через прокси.

Что будет с заметками, если сервер временно недоступен?

Ничего страшного — Joplin хранит полную копию заметок локально на устройстве и просто откладывает синхронизацию до следующего успешного подключения. Данные не теряются, работа с заметками не блокируется.

Нужен ли email/SMTP, если я один пользователь сервера?

Нет, MAILER_ENABLED: "false" вполне рабочий вариант — просто вы не сможете сбросить пароль через форму «забыли пароль» и придётся менять его вручную из-под администратора в веб-интерфейсе.

Чем Joplin Server отличается от хранения заметок через WebDAV или Nextcloud?

Joplin Server — специализированный протокол синхронизации именно под Joplin: умеет диффы, конфликт-резолюшн и версии заметок на уровне приложения. WebDAV и синхронизация через Nextcloud тоже работают с Joplin, но менее эффективны на больших коллекциях заметок и не дают такой же гранулярной истории изменений.

Сколько пользователей выдержит один такой сервер?

Для семьи, команды из нескольких человек или личного использования на нескольких устройствах минимальный VPS справляется без проблем — узкое место обычно не CPU, а объём вложений на диске, если в заметки заливают много изображений и файлов.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →