Collection of NotesDockerDocker Install Seafile
Collection of NotesDockerDocker Install Seafile
Docker

Docker Install Seafile

1. Project Setup

Seafile is deployed in docker with integration of SeaDoc and OnlyOffice.

.
├── caddy.yml
├── .env
├── onlyoffice.yml
├── opt
│   ├── onlyoffice
│   ├── seafile-caddy
│   ├── seafile-data
│   └── seafile-mysql
├── seadoc.yml
└── seafile-server.yml

Folder Structure

  1. Seafile Server: main server with Nginx as web server and MySQL as database.
  2. Caddy is the reverse proxy.
  3. Seadoc is the Wiki component.
  4. OnlyOffice is the document server.
  5. Opt folder stores all the persistent files for the 4 components.
  6. .env stores the variables for different yml files.

2. Configs

YAML
COMPOSE_FILE='seafile-server.yml,caddy.yml,seadoc.yml,onlyoffice.yml'
COMPOSE_PATH_SEPARATOR=','


SEAFILE_IMAGE=seafileltd/seafile-mc:12.0-latest
SEAFILE_DB_IMAGE=mariadb:10.11
SEAFILE_MEMCACHED_IMAGE=memcached:1.6.29
SEAFILE_CADDY_IMAGE=lucaslorentz/caddy-docker-proxy:2.9-alpine

SEAFILE_VOLUME=./opt/seafile-data
SEAFILE_MYSQL_VOLUME=./opt/seafile-mysql/db
SEAFILE_CADDY_VOLUME=./opt/seafile-caddy

SEAFILE_MYSQL_DB_HOST=db
INIT_SEAFILE_MYSQL_ROOT_PASSWORD=ROOT_PASSWORD
SEAFILE_MYSQL_DB_USER=seafile
SEAFILE_MYSQL_DB_PASSWORD=PASSWORD

TIME_ZONE=Asia/Shanghai

JWT_PRIVATE_KEY=BdWciKsfuUT7fsxGN04N0pfxA6THpOqV2ShtFJzr

SEAFILE_SERVER_HOSTNAME=10.124.159.18
SEAFILE_SERVER_PROTOCOL=http

INIT_SEAFILE_ADMIN_EMAIL=me@example.com
INIT_SEAFILE_ADMIN_PASSWORD=asecret2025


SEADOC_IMAGE=seafileltd/sdoc-server:1.0-latest
SEADOC_VOLUME=/opt/seadoc-data

ENABLE_SEADOC=true


NOTIFICATION_SERVER_IMAGE=seafileltd/notification-server:12.0-latest
NOTIFICATION_SERVER_VOLUME=/opt/notification-data

# OnlyOffice image
ONLYOFFICE_IMAGE=onlyoffice/documentserver:8.1.0.1

# Persistent storage directory of OnlyOffice
ONLYOFFICE_VOLUME=./opt/onlyoffice

# OnlyOffice document server port
ONLYOFFICE_PORT=6233

# jwt secret, generated by `pwgen -s 40 1`
ONLYOFFICE_JWT_SECRET=BdWciKsfuUT7fsxGN04N0pfxA6THpOqV2ShtFJzr
YAML
services:
  db:
    image: ${SEAFILE_DB_IMAGE:-mariadb:10.11}
    container_name: seafile-mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-}
      - MYSQL_LOG_CONSOLE=true
      - MARIADB_AUTO_UPGRADE=1
    volumes:
      - "${SEAFILE_MYSQL_VOLUME:-/opt/seafile-mysql/db}:/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

  memcached:
    image: ${SEAFILE_MEMCACHED_IMAGE:-memcached:1.6.29}
    container_name: seafile-memcached
    entrypoint: memcached -m 256
    networks:
      - seafile-net

  seafile:
    image: ${SEAFILE_IMAGE:-seafileltd/seafile-mc:12.0-latest}
    container_name: seafile
    # ports:
    #   - "80:80"
    volumes:
      - ${SEAFILE_VOLUME:-/opt/seafile-data}:/shared
    environment:
      - DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db}
      - DB_PORT=${SEAFILE_MYSQL_DB_PORT:-3306}
      - DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}
      - DB_ROOT_PASSWD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-}
      - DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
      - SEAFILE_MYSQL_DB_CCNET_DB_NAME=${SEAFILE_MYSQL_DB_CCNET_DB_NAME:-ccnet_db}
      - SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME:-seafile_db}
      - SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME:-seahub_db}
      - TIME_ZONE=${TIME_ZONE:-Etc/UTC}
      - INIT_SEAFILE_ADMIN_EMAIL=${INIT_SEAFILE_ADMIN_EMAIL:-me@example.com}
      - INIT_SEAFILE_ADMIN_PASSWORD=${INIT_SEAFILE_ADMIN_PASSWORD:-asecret}
      - SEAFILE_SERVER_HOSTNAME=${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
      - SEAFILE_SERVER_PROTOCOL=${SEAFILE_SERVER_PROTOCOL:-http}
      - SITE_ROOT=${SITE_ROOT:-/}
      - NON_ROOT=${NON_ROOT:-false}
      - JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
      - SEAFILE_LOG_TO_STDOUT=${SEAFILE_LOG_TO_STDOUT:-false}
      - ENABLE_SEADOC=${ENABLE_SEADOC:-true}
      - SEADOC_SERVER_URL=${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}/sdoc-server
    labels:
      caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
      caddy.reverse_proxy: "{{upstreams 80}}"
    depends_on:
      db:
        condition: service_healthy
      memcached:
        condition: service_started
    networks:
      - seafile-net

networks:
  seafile-net:
    name: seafile-net
YAML
services:

  caddy:
    image: ${SEAFILE_CADDY_IMAGE:-lucaslorentz/caddy-docker-proxy:2.9-alpine}
    restart: unless-stopped
    container_name: seafile-caddy
    ports:
      - 80:80
      - 443:443
    environment:
      - CADDY_INGRESS_NETWORKS=seafile-net
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ${SEAFILE_CADDY_VOLUME:-/opt/seafile-caddy}:/data/caddy
    networks:
      - seafile-net
    healthcheck:
      test: ["CMD-SHELL", "curl --fail http://localhost:2019/metrics || exit 1"]
      start_period: 20s
      interval: 20s
      timeout: 5s
      retries: 3

networks:
  seafile-net:
    name: seafile-net
YAML
services:

  seadoc:
    image: ${SEADOC_IMAGE:-seafileltd/sdoc-server:1.0-latest}
    container_name: seadoc
    volumes:
      - ${SEADOC_VOLUME:-/opt/seadoc-data/}:/shared
    # ports:
    #   - "80:80"
    environment:
      - DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db}
      - DB_PORT=${SEAFILE_MYSQL_DB_PORT:-3306}
      - DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}
      - DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
      - DB_NAME=${SEADOC_MYSQL_DB_NAME:-seahub_db}
      - TIME_ZONE=${TIME_ZONE:-Etc/UTC}
      - JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
      - NON_ROOT=${NON_ROOT:-false}
      - SEAHUB_SERVICE_URL=${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
    labels:
      caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
      caddy.@ws.0_header: "Connection *Upgrade*"
      caddy.@ws.1_header: "Upgrade websocket"
      caddy.0_reverse_proxy: "@ws {{upstreams 80}}"
      caddy.1_handle_path: "/socket.io/*"
      caddy.1_handle_path.0_rewrite: "* /socket.io{uri}"
      caddy.1_handle_path.1_reverse_proxy: "{{upstreams 80}}"
      caddy.2_handle_path: "/sdoc-server/*"
      caddy.2_handle_path.0_rewrite: "* {uri}"
      caddy.2_handle_path.1_reverse_proxy: "{{upstreams 80}}"
    depends_on:
      db:
        condition: service_healthy
    networks:
      - seafile-net

networks:
  seafile-net:
    name: seafile-net
YAML
---
services:
  caddy:
    ports:
      - ${ONLYOFFICE_PORT:-6233}:${ONLYOFFICE_PORT:-6233}

  onlyoffice:
    image: ${ONLYOFFICE_IMAGE:-onlyoffice/documentserver:8.1.0.1}
    restart: unless-stopped
    container_name: seafile-onlyoffice
    environment:
      # old environment config from .venv
      # - DB_TYPE=${DB_TYPE:-mariadb}
      # - DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db}
      # - DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}
      # - DB_PWD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
      # - JWT_ENABLED=true
      # - JWT_SECRET=${ONLYOFFICE_JWT_SECRET:?Variable is not set or empty}
          
      # new environment config after creating new db, username and password in mysql.
      - DB_TYPE=mariadb
      - DB_HOST=db
      - DB_NAME=onlyoffice
      - DB_USER=onlyoffice
      - DB_PWD=onlyofficepass
      - JWT_ENABLED=true
      - JWT_SECRET=${ONLYOFFICE_JWT_SECRET:?Variable is not set or empty}
    volumes:
      - ${ONLYOFFICE_VOLUME:-./opt/onlyoffice}/logs:/var/log/onlyoffice
      - ${ONLYOFFICE_VOLUME:-./opt/onlyoffice}/data:/var/www/onlyoffice/Data
      - ${ONLYOFFICE_VOLUME:-./opt/onlyoffice}/lib:/var/lib/onlyoffice
    labels:
      caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}:${ONLYOFFICE_PORT:-6233}
      caddy.reverse_proxy: "{{upstreams}}"
    networks:
      - seafile-net

networks:
  seafile-net:
    name: seafile-net

OnlyOffice configs needs to be added to Seafile server settings at opt/seafile-data/seafile/conf/seahub_settings.py.

Plaintext
# -*- coding: utf-8 -*-
SECRET_KEY = "m_lxv&xre6kt4o+ss3)b1=q*tsl3ua3r7xe+%hxes%@952tezb"
SERVICE_URL = "http://10.124.159.18"

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'seahub_db',
        'USER': 'seafile',
        'PASSWORD': 'PASSWORD',
        'HOST': 'db',
        'PORT': '3306',
        'OPTIONS': {'charset': 'utf8mb4'},
    }
}


CACHES = {
    'default': {
        'BACKEND': 'django_pylibmc.memcached.PyLibMCCache',
        'LOCATION': 'memcached:11211',
    },
    'locmem': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    },
}
COMPRESS_CACHE_BACKEND = 'locmem'

TIME_ZONE = 'Asia/Shanghai'
FILE_SERVER_ROOT = 'http://10.124.159.18/seafhttp'

ENABLE_ONLYOFFICE = True
ONLYOFFICE_APIJS_URL = 'http://10.124.159.18:6233/web-apps/apps/api/documents/api.js'
ONLYOFFICE_FILE_SERVER_ROOT = 'http://10.124.159.18:6233/'
ONLYOFFICE_FILE_EXTENSION = ('doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'odt', 'fodt', 'odp', 'fodp', 'ods', 'fods', 'csv', 'ppsx', 'pps')
ONLYOFFICE_EDIT_FILE_EXTENSION = ('docx', 'pptx', 'xlsx')
ONLYOFFICE_JWT_SECRET = 'BdWciKsfuUT7fsxGN04N0pfxA6THpOqV2ShtFJzr'

3. Troubleshoot

3.1 Database Issue

Plaintext
seafile-mysql       | 2025-04-30  6:55:16 0 [Warning] You need to use --log-bin to make --expire-logs-days or --binlog-expire-logs-seconds work.
seafile-mysql       | 2025-04-30  6:55:16 0 [Note] Server socket created on IP: '0.0.0.0'.
seafile-mysql       | 2025-04-30  6:55:16 0 [Note] Server socket created on IP: '::'.
seafile-mysql       | 2025-04-30  6:55:16 0 [Note] mariadbd: ready for connections.
seafile-mysql       | Version: '10.11.10-MariaDB-ubu2204'  socket: '/run/mysqld/mysqld.sock'  port: 3306  mariadb.org binary distribution
seafile-mysql       | 2025-04-30  6:55:16 0 [Note] InnoDB: Buffer pool(s) load completed at 250430  6:55:16
seafile-mysql       | 2025-04-30  6:55:18 3 [Warning] Aborted connection 3 to db: 'unconnected' user: 'unauthenticated' host: '172.23.0.3' (This connection closed normally without authentication)
seafile-mysql       | 2025-04-30  6:55:18 5 [Warning] Access denied for user 'seafile'@'%' to database 'onlyoffice'
seafile-mysql       | 2025-04-30  6:55:33 8 [Warning] Aborted connection 8 to db: 'unconnected' user: 'unauthenticated' host: '172.23.0.3' (This connection closed normally without authentication)
seafile-mysql       | 2025-04-30  6:55:41 11 [Warning] Access denied for user 'seafile'@'%' to database 'onlyoffice'
seafile-onlyoffice  | Generating presentation themes, please wait...Done
seafile-onlyoffice  | Generating js caches, please wait...Done
seafile-onlyoffice  | ds:docservice: stopped
seafile-onlyoffice  | ds:docservice: started
seafile-mysql       | 2025-04-30  6:56:59 51 [Warning] Access denied for user 'seafile'@'%' to database 'onlyoffice'

This indicates user seafile can’t access database onlyoffice. However, in MySQL there is no database onlyoffice.

The onlyoffice/documentserver image expects its own database, typically called onlyoffice, and it does not use Seafile’s seafile database user.

Plaintext
##### Go into MySQL container #####

docker exec -it seafile-mysql mysql -uroot -p

##### Add onlyoffice user and only office database #####

CREATE DATABASE onlyoffice;
CREATE USER 'onlyoffice'@'%' IDENTIFIED BY 'onlyofficepass';
GRANT ALL PRIVILEGES ON onlyoffice.* TO 'onlyoffice'@'%';
FLUSH PRIVILEGES;

##### Change the DB environment for ONLYOFFICE in onlyoffice.yml #####

environment:
  - DB_TYPE=mariadb
  - DB_HOST=db
  - DB_NAME=onlyoffice
  - DB_USER=onlyoffice
  - DB_PWD=onlyofficepass
  - JWT_ENABLED=true
  - JWT_SECRET=${ONLYOFFICE_JWT_SECRET:?Variable is not set or empty}
  
##### Restart OnlyOffice container #####
  
docker compose -f onlyoffice.yml down
docker compose -f onlyoffice.yml up -d

3.2 OnlyOffice Document Server Issue

Plaintext
seafile-onlyoffice  | 2025/04/30 06:46:22 [error] 8955#8955: *97 connect() failed (111: Unknown error) while connecting to upstream, client: 172.22.0.4, server: , request: "GET /8.1.0-169/doc/3c60f0640004930ae2a0/c/?shardkey=3c60f0640004930ae2a0&EIO=4&transport=polling&t=PQ5Sax0 HTTP/1.1", upstream: "http://127.0.0.1:8000/doc/3c60f0640004930ae2a0/c/?shardkey=3c60f0640004930ae2a0&EIO=4&transport=polling&t=PQ5Sax0", host: "10.124.159.18:6233", referrer: "http://10.124.159.18:6233/8.1.0-169/web-apps/apps/spreadsheeteditor/main/index.html?_dc=8.1.0-169&lang=en&customer=ONLYOFFICE&type=desktop&frameEditorId=placeholder&isForm=false&parentOrigin=http://10.124.159.18&fileType=xlsx"

ONLYOFFICE Document Server is not running inside the same container – the 127.0.0.1 address refers to localhost within the seafile-onlyoffice container.

  • ONLYOFFICE is a standalone service on its own.
  • It runs the document server itself.
  • Seafile communicates with it over the network, not via localhost inside the same container.

This issue typically arises when Seafile is misconfigured to use http://127.0.0.1:8000 as the ONLYOFFICE integration endpoint.

You must set the correct ONLYOFFICE integration URL in Seafile so it talks to the correct container via Docker network or external URL.

Plaintext
# add the following to 'opt/seafile-data/seafile/conf/seahub_settings.py'

ONLYOFFICE_FILE_SERVER_ROOT = 'http://10.124.159.18:6233/'

# restart seafile server

docker compose -f seafile-server.yml restart seafile

4. Reference

  1. https://manual.seafile.com/12.0/setup/setup_ce_by_docker/
  2. https://manual.seafile.com/12.0/extension/only_office/

Leave a Reply

Your email address will not be published. Required fields are marked *