Делюсь полностью рабочими конфигурационными файлами, которые я выстрадал и проверил в бою.
Это полноценная замена Nextcloud на базе Seafile — более лёгкая, быстрая и с отличной синхронизацией. В комплекте:
-
Seafile (Community Edition 13.0.18) — само хранилище, работает как часы.
-
OnlyOffice (8.1.0.1) — онлайн-редактор документов, таблиц и презентаций, идеально встраивается в Seafile.
-
SeaDoc — редактор документов (ещё одна фича Seafile).
-
Notification Server — для уведомлений и мониторинга (ping-эндпоинт).
Всё завернуто в Docker Compose, все версии образов зафиксированы (исключить внезапные обновы).
Настройки вынесены в единый .env — легко править под себя.
Как использовать
-
Подготовьте чистую VM (Debian 12) с достаточным дисковым пространством (минимум 20–30 ГБ, или столько, сколько вам нужно облачного пространства).
-
Установите Docker и Docker Compose (если ещё не сделано).
-
Создайте директорию (
/opt/seafile) и поместите в неё все файлы:-
.env(переименуйте, подставьте свои данные) -
seafile-server.yml -
seadoc.yml -
onlyoffice.yml -
notification-server.yml
-
-
Отредактируйте
.env:-
Замените
your.domain.ruна ваш реальный домен (например,drive.example.com). -
Сгенерируйте надёжные пароли для
JWT_PRIVATE_KEY,SEAFILE_MYSQL_DB_PASSWORD,REDIS_PASSWORD,INIT_SEAFILE_MYSQL_ROOT_PASSWORD,ONLYOFFICE_JWT_SECRET.
Можно использовать команду (в терминале):
openssl rand -base64 32 | tr -dc 'a-zA-Z' | head -c 32; echo -
Укажите ваш email и пароль администратора Seafile (
INIT_SEAFILE_ADMIN_EMAIL,INIT_SEAFILE_ADMIN_PASSWORD). -
Если у вас другой часовой пояс, измените
TIME_ZONE.
-
-
Запустите стек:
cd /opt/seafile
docker compose up -d
- Проверьте логи и доступность сервисов:
-
Seafile:
http://ваш-ip:8080(после настройки Traefik будет доступен по домену) -
OnlyOffice:
http://ваш-ip:6233/welcome/ -
Notification Server:
http://ваш-ip:8083/ping(должен ответить{"ret":"pong"})
Доступ из интернета (опционально)
Я использую Traefik как внешний прокси с автоматическими SSL-сертификатами.
Вы можете адаптировать конфигурацию под свой прокси (nginx, Caddy и т.д.).
Для Traefik пример конфига:
http:
routers:
seafile-main:
rule: "Host(`seafile.example.com`) && !PathPrefix(`/notification`)"
entryPoints: ["websecure"]
tls:
certResolver: letsencrypt
options: modern@file
middlewares:
- chain-public@file
- seafile-headers@file
service: seafile-main
seafile-notification:
rule: "Host(`seafile.example.com`) && PathPrefix(`/notification`)"
entryPoints: ["websecure"]
tls:
certResolver: letsencrypt
options: modern@file
middlewares:
- chain-public@file
- notification-headers@file
service: seafile-notification
onlyoffice:
rule: "Host(`office.example.com`)"
entryPoints: ["websecure"]
tls:
certResolver: letsencrypt
options: modern@file
middlewares:
- chain-public@file
- onlyoffice-headers@file
service: onlyoffice
services:
seafile-main:
loadBalancer:
servers:
- url: "http://<IP_вашего_сервера_Seafile>:8080"
seafile-notification:
loadBalancer:
servers:
- url: "http://<IP_вашего_сервера_Seafile>:8083"
healthCheck:
path: /ping
interval: 10s
timeout: 3s
onlyoffice:
loadBalancer:
passHostHeader: true
servers:
- url: "http://<IP_вашего_сервера_Seafile>:6233"
middlewares:
seafile-headers:
headers:
customRequestHeaders:
X-Forwarded-Proto: "https"
X-Forwarded-Host: "seafile.example.com"
customResponseHeaders:
X-Frame-Options: "SAMEORIGIN"
Content-Security-Policy: "frame-ancestors 'self' https://seafile.example.com"
notification-headers:
headers:
customRequestHeaders:
X-Forwarded-Proto: "https"
X-Forwarded-Host: "seafile.example.com"
onlyoffice-headers:
headers:
customRequestHeaders:
X-Forwarded-Proto: "https"
X-Forwarded-Host: "office.example.com"
customResponseHeaders:
X-Frame-Options: ""
Content-Security-Policy: "frame-ancestors 'self' https://seafile.example.com"
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
browserXssFilter: true
contentTypeNosniff: true
referrerPolicy: "strict-origin-when-cross-origin"
permissionsPolicy: "geolocation=(self), camera=(self), microphone=(self)"
Пояснения для сообщества
-
Замените
seafile.example.comиoffice.example.comна ваши реальные домены. -
Вместо
<IP_вашего_сервера_Seafile>укажите локальный IP машины, на которой запущен Seafile (например,192.168.1.100). -
chain-public@file– это ссылка на цепочку middleware, которая обычно включает CrowdSec, rate limit и базовые заголовки безопасности. Вы можете определить её в отдельном файле или заменить на свои middleware. -
modern@file– TLS-опции, например:
tls:
options:
modern:
minVersion: VersionTLS12
- Middleware
seafile-headers,notification-headersиonlyoffice-headersуже описаны здесь же – они добавляют необходимые заголовки для корректной работы Seafile и OnlyOffice (в частности, для работы OnlyOffice в iframe).
Состав файлов
| Файл | Назначение |
|---|---|
.env |
Все настройки (пароли, пути, домены) |
seafile-server.yml |
Основной стек: MariaDB, Redis, Seafile |
seadoc.yml |
Сервис SeaDoc |
onlyoffice.yml |
OnlyOffice Document Server |
notification-server.yml |
Сервер уведомлений (нужен для мониторинга) |
Важно
-
В файлах нет чувствительных данных — только шаблоны. Все секреты вы задаёте сами в
.env. -
Версии образов жёстко зафиксированы (никаких
latest), чтобы ничего не сломалось при обновлениях. -
Если вам нужен антивирус (ClamAV) — официальная интеграция в Docker-версии Seafile не работает (отсутствует
clamdscanв контейнере). Я отказался от неё, но вы можете попробовать самостоятельно.
Версия от 28.02.2026, проверено в работе.
notification-server.yml
services:
notification-server:
image: ${NOTIFICATION_SERVER_IMAGE}
container_name: notification-server
restart: unless-stopped
volumes:
- ${SEAFILE_VOLUME}/seafile/logs:/shared/seafile/logs
ports:
- "8083:8083"
environment:
- SEAFILE_MYSQL_DB_HOST=${SEAFILE_MYSQL_DB_HOST}
- SEAFILE_MYSQL_DB_PORT=${SEAFILE_MYSQL_DB_PORT}
- SEAFILE_MYSQL_DB_USER=${SEAFILE_MYSQL_DB_USER}
- SEAFILE_MYSQL_DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
- SEAFILE_MYSQL_DB_CCNET_DB_NAME=${SEAFILE_MYSQL_DB_CCNET_DB_NAME}
- SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME}
- JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
- SEAFILE_LOG_TO_STDOUT=${SEAFILE_LOG_TO_STDOUT}
- NOTIFICATION_SERVER_LOG_LEVEL=${NOTIFICATION_SERVER_LOG_LEVEL}
- NOTIFICATION_SERVER_URL=${NOTIFICATION_SERVER_URL}
- INNER_NOTIFICATION_SERVER_URL=${INNER_NOTIFICATION_SERVER_URL}
depends_on:
db:
condition: service_healthy
seafile:
condition: service_healthy
networks:
- seafile-net
networks:
seafile-net:
name: seafile-net
artidev@debian-external:/opt/seafile$ cat onlyoffice.yml
services:
onlyoffice:
image: ${ONLYOFFICE_IMAGE}
restart: unless-stopped
container_name: seafile-onlyoffice
ports:
- "${ONLYOFFICE_PORT}:80"
environment:
- JWT_ENABLED=true
- JWT_SECRET=${ONLYOFFICE_JWT_SECRET:?Variable is not set or empty}
- PROXY_TRUSTED=true
- PROXY_PROTOCOL=https
- PROXY_HOST=office.yourdomain.ru # заменить на свой реальный
- PROXY_PORT=443
volumes:
- ${ONLYOFFICE_VOLUME}/logs:/var/log/onlyoffice
- ${ONLYOFFICE_VOLUME}/data:/var/www/onlyoffice/Data
- ${ONLYOFFICE_VOLUME}/lib:/var/lib/onlyoffice
networks:
- seafile-net
networks:
seafile-net:
name: seafile-net
seadoc.yml
services:
seadoc:
image: ${SEADOC_IMAGE}
container_name: seadoc
restart: unless-stopped
volumes:
- ${SEADOC_VOLUME}:/shared
# ports:
# - "80:80"
environment:
- DB_HOST=${SEAFILE_MYSQL_DB_HOST}
- DB_PORT=${SEAFILE_MYSQL_DB_PORT}
- DB_USER=${SEAFILE_MYSQL_DB_USER}
- DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
- DB_NAME=${SEADOC_MYSQL_DB_NAME}
- TIME_ZONE=${TIME_ZONE}
- JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
- NON_ROOT=${NON_ROOT}
- SEAHUB_SERVICE_URL=${SEAFILE_SERVICE_URL}
depends_on:
db:
condition: service_healthy
networks:
- seafile-net
networks:
seafile-net:
name: seafile-net
seafile-server.yml
services:
db:
image: ${SEAFILE_DB_IMAGE}
container_name: seafile-mysql
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD}
- MYSQL_LOG_CONSOLE=true
- MARIADB_AUTO_UPGRADE=1
volumes:
- "${SEAFILE_MYSQL_VOLUME}:/var/lib/mysql"
networks:
- seafile-net
healthcheck:
test:
[
"CMD",
"/usr/local/bin/healthcheck.sh",
"--connect",
"--mariadbupgrade",
"--innodb_initialized",
]
interval: 20s
start_period: 30s
timeout: 5s
retries: 10
redis:
image: ${SEAFILE_REDIS_IMAGE}
container_name: seafile-redis
restart: unless-stopped
command:
- /bin/sh
- -c
- redis-server --requirepass "$$REDIS_PASSWORD"
environment:
- REDIS_PASSWORD=${REDIS_PASSWORD}
networks:
- seafile-net
seafile:
image: ${SEAFILE_IMAGE}
container_name: seafile
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ${SEAFILE_VOLUME}:/shared
environment:
- SEAFILE_MYSQL_DB_HOST=${SEAFILE_MYSQL_DB_HOST}
- SEAFILE_MYSQL_DB_PORT=${SEAFILE_MYSQL_DB_PORT}
- SEAFILE_MYSQL_DB_USER=${SEAFILE_MYSQL_DB_USER}
- SEAFILE_MYSQL_DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
- INIT_SEAFILE_MYSQL_ROOT_PASSWORD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD}
- SEAFILE_MYSQL_DB_CCNET_DB_NAME=${SEAFILE_MYSQL_DB_CCNET_DB_NAME}
- SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME}
- SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME}
- TIME_ZONE=${TIME_ZONE}
- INIT_SEAFILE_ADMIN_EMAIL=${INIT_SEAFILE_ADMIN_EMAIL}
- INIT_SEAFILE_ADMIN_PASSWORD=${INIT_SEAFILE_ADMIN_PASSWORD}
- SEAFILE_SERVER_HOSTNAME=${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
- SEAFILE_SERVER_PROTOCOL=${SEAFILE_SERVER_PROTOCOL}
- SITE_ROOT=${SITE_ROOT}
- NON_ROOT=${NON_ROOT}
- JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
- SEAFILE_LOG_TO_STDOUT=${SEAFILE_LOG_TO_STDOUT}
- ENABLE_GO_FILESERVER=${ENABLE_GO_FILESERVER}
- ENABLE_SEADOC=${ENABLE_SEADOC}
- SEADOC_SERVER_URL=${SEAFILE_SERVER_PROTOCOL}://${SEAFILE_SERVER_HOSTNAME}/sdoc-server
- CACHE_PROVIDER=${CACHE_PROVIDER}
- REDIS_HOST=${REDIS_HOST}
- REDIS_PORT=${REDIS_PORT}
- REDIS_PASSWORD=${REDIS_PASSWORD}
- MEMCACHED_HOST=${MEMCACHED_HOST}
- MEMCACHED_PORT=${MEMCACHED_PORT}
- ENABLE_NOTIFICATION_SERVER=${ENABLE_NOTIFICATION_SERVER}
- INNER_NOTIFICATION_SERVER_URL=${INNER_NOTIFICATION_SERVER_URL}
- NOTIFICATION_SERVER_URL=${NOTIFICATION_SERVER_URL}
- ENABLE_SEAFILE_AI=${ENABLE_SEAFILE_AI}
- SEAFILE_AI_SERVER_URL=${SEAFILE_AI_SERVER_URL}
- SEAFILE_AI_SECRET_KEY=${JWT_PRIVATE_KEY}
- MD_FILE_COUNT_LIMIT=${MD_FILE_COUNT_LIMIT}
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- seafile-net
networks:
seafile-net:
name: seafile-net
.env
COMPOSE_PATH_SEPARATOR=,
COMPOSE_FILE=seafile-server.yml,seadoc.yml,onlyoffice.yml,notification-server.yml
SEAFILE_IMAGE=seafileltd/seafile-mc:13.0.18
SEAFILE_DB_IMAGE=mariadb:10.11.16
SEAFILE_REDIS_IMAGE=redis:8.6.1
SEADOC_IMAGE=seafileltd/sdoc-server:2.0.9
NOTIFICATION_SERVER_IMAGE=seafileltd/notification-server:13.0.10
ONLYOFFICE_IMAGE=onlyoffice/documentserver:8.1.0.1
BASIC_STORAGE_PATH=/opt
SEAFILE_VOLUME=$BASIC_STORAGE_PATH/seafile-data
SEAFILE_MYSQL_VOLUME=$BASIC_STORAGE_PATH/seafile-mysql/db
SEADOC_VOLUME=$BASIC_STORAGE_PATH/seadoc-data
ONLYOFFICE_VOLUME=$BASIC_STORAGE_PATH/onlyoffice
SEAFILE_SERVER_HOSTNAME=your.domain.ru
SEAFILE_SERVER_PROTOCOL=https
TIME_ZONE=Europe/Moscow
JWT_PRIVATE_KEY=password must be at least 32 characters long
SEAFILE_MYSQL_DB_HOST=db
SEAFILE_MYSQL_DB_PORT=3306
SEAFILE_MYSQL_DB_USER=seafile
SEAFILE_MYSQL_DB_PASSWORD=password
SEAFILE_MYSQL_DB_CCNET_DB_NAME=ccnet_db
SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=seafile_db
SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=seahub_db
CACHE_PROVIDER=redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=password
INIT_SEAFILE_MYSQL_ROOT_PASSWORD=password
INIT_SEAFILE_ADMIN_EMAIL=your e-mail
INIT_SEAFILE_ADMIN_PASSWORD=e-mail application password
ENABLE_SEADOC=true
ENABLE_NOTIFICATION_SERVER=true
NOTIFICATION_SERVER_LOG_LEVEL=info
NOTIFICATION_SERVER_URL=https://your.domain.ru/notification
INNER_NOTIFICATION_SERVER_URL=https://notification-server:8083
ENABLE_SEAFILE_AI=false
MD_FILE_COUNT_LIMIT=100000
ONLYOFFICE_PORT=6233
ONLYOFFICE_JWT_SECRET=password must be at least 32 characters long
SEADOC_MYSQL_DB_NAME=seahub_db
SEAFILE_SERVICE_URL=https://seafile
SEAFILE_LOG_TO_STDOUT=false
ENABLE_GO_FILESERVER=true
SITE_ROOT=/
NON_ROOT=false
SEAFILE_AI_SERVER_URL=https://seafile-ai:8888
MEMCACHED_HOST=
MEMCACHED_PORT=
Команды для генерации случайного набора символов:
openssl rand -base64 32 | tr -dc 'a-zA-Z' | head -c 32; echo
Если нужно больше/меньше символов:
Для 16 букв:
openssl rand -base64 16 | tr -dc 'a-zA-Z' | head -c 16; echo
Для 24 букв:
openssl rand -base64 18 | tr -dc 'a-zA-Z' | head -c 24; echo
Для 64 букв:
openssl rand -base64 48 | tr -dc 'a-zA-Z' | head -c 64; echo
После развертывания стека, нужно отредактировать системный файл конфигурации: seahub_settings.py
Пример моего рабочего seahub_settings.py имеет следующий вид.
# -*- coding: utf-8 -*-
SECRET_KEY = будет сгененрирован в процессе установки
TIME_ZONE = 'Europe/Moscow'
EMAIL_USE_SSL = True
EMAIL_HOST = '' # smpt server
EMAIL_HOST_USER = '' # username and domain
EMAIL_HOST_PASSWORD = '' # password
EMAIL_PORT =
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
SERVER_EMAIL = EMAIL_HOST_USER
ENABLE_ONLYOFFICE = True
ONLYOFFICE_APIJS_URL = 'https://office.yourdomain.ru/web-apps/apps/api/documents/api.js'
ONLYOFFICE_JWT_SECRET = '' # должен совпадать с .env
ONLYOFFICE_FILE_EXTENSION = ('doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'odt', 'fodt', 'odp', 'fodp', 'ods', 'fods', 'ppsx', 'pps', 'csv')
ONLYOFFICE_EDIT_FILE_EXTENSION = ('docx', 'pptx', 'xls', 'csv')
OFFICE_PREVIEW_MAX_SIZE = 30 * 1024 * 1024
ONLYOFFICE_FORCE_SAVE = True
SERVICE_URL = 'https://drive.yourdomain.ru'
FILE_SERVER_ROOT = 'https://drive.yourdomain.ru/seafhttp'
Как использовать
- Отредактируйте содержимое файла, который находится в папке
confвашего Seafile (обычно/opt/seafile-data/seafile/conf/) с помощью команды:
sudo /opt/seafile-data/seafile/conf/seahub_settings.py
-
Замените все
yourdomain.ruна свой реальный домен. -
Перезапустите контейнер Seafile, чтобы изменения вступили в силу:
docker restart seafile

