Migration from CE to Pro 13.0.11 fails with "Unknown column 'is_department_owner'"

Hello everyone,

I’m trying to migrate my Seafile instance from the Community Edition to the Professional Edition and I’m running into a database-related error that prevents the server from starting.

My goal is to perform a simple migration first, without enabling full-text search (Elasticsearch) for now.

Environment:

  • Seafile Version: 13.0.11 (migrating from CE 13.0.11 to Pro 13.0.11)
  • Deployment: Using Docker Compose

My Process:
My migration process was to simply change the image name in my docker-compose.yml file for the seafile service:

  • From: seafileltd/seafile-mc:13.0-latest
  • To: seafileltd/seafile-pro-mc:13.0-latest

After making this change and running docker-compose up -d, the seafile container fails to start.

Error Log:
The container log shows the following critical error, indicating a missing database column. It seems a database schema upgrade is required.

seafile-1  | 2025-10-14T12:55:22.796376000Z [seaf-server] [2025-10-14 08:55:22] [WARNING] ../common/seaf-db-mysql.c(239): Failed to prepare sql SELECT COUNT(id) FROM EmailUser WHERE is_active = 1 AND is_department_owner = 0: Unknown column 'is_department_owner' in 'WHERE'
seafile-1  | 2025-10-14T12:55:22.796412000Z [seaf-server] [2025-10-14 08:55:22] [WARNING] ../common/user-mgr.c(97): Failed to get user number from DB.
seafile-1  | 2025-10-14T12:55:22.796444000Z [seaf-server] [2025-10-14 08:55:22] [WARNING] seafile-session.c(699): Failed to init user manager.
seafile-1  | 2025-10-14T12:55:23.769361000Z Failed to start seafile server
seafile-1  | 2025-10-14T12:55:23.779543000Z Traceback (most recent call last):
seafile-1  | 2025-10-14T12:55:23.779772000Z   File "/scripts/start.py", line 94, in 
seafile-1  | 2025-10-14T12:55:23.780441000Z     main()
seafile-1  | 2025-10-14T12:55:23.780810000Z   File "/scripts/start.py", line 79, in main
seafile-1  | 2025-10-14T12:55:23.781011000Z     call('{} start'.format(get_script('seafile.sh')))
seafile-1  | 2025-10-14T12:55:23.781240000Z   File "/scripts/utils.py", line 71, in call
seafile-1  | 2025-10-14T12:55:23.782362000Z     return subprocess.check_call(*a, **kw)
seafile-1  | 2025-10-14T12:55:23.782410000Z            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
seafile-1  | 2025-10-14T12:55:23.782442000Z   File "/usr/lib/python3.12/subprocess.py", line 413, in check_call
seafile-1  | 2025-10-14T12:55:23.782489000Z     raise CalledProcessError(retcode, cmd)
seafile-1  | 2025-10-14T12:55:23.782514000Z subprocess.CalledProcessError: Command '/opt/seafile/seafile-pro-server-13.0.11/seafile.sh start' returned non-zero exit status 1.

My docker-compose.yml:
Here is my full docker-compose.yml file (with the Pro image configured in the seafile service).

services:
  db:
    image: mariadb:10
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${INIT_SEAFILE_MYSQL_ROOT_PASSWORD}
      MYSQL_LOG_CONSOLE: true
      MARIADB_AUTO_UPGRADE: 1
    volumes:
      - ${DATA_PATH}/seafile/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

  redis:
    image: redis
    restart: unless-stopped
    command: ["sh", "-c", "redis-server --requirepass \"$$REDIS_PASSWORD\""]
    environment:
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    volumes:
      - ${DATA_PATH}/seafile/redis:/data
    networks:
      - seafile_net

  seafile:
    image: seafileltd/seafile-pro-mc:13.0-latest
    restart: unless-stopped
    volumes:
      - ${SEAFILE_PATH}/files:/shared
    environment:
      SEAFILE_MYSQL_DB_HOST: db
      SEAFILE_MYSQL_DB_PORT: 3306
      SEAFILE_MYSQL_DB_USER: seafile
      SEAFILE_MYSQL_DB_PASSWORD: ${SEAFILE_MYSQL_DB_PASSWORD}
      # INIT_SEAFILE_MYSQL_ROOT_PASSWORD: ${INIT_SEAFILE_MYSQL_ROOT_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
      TIME_ZONE: ${TIME_ZONE:-Etc/UTC}
      SEAFILE_LOG_TO_STDOUT: ${SEAFILE_LOG_TO_STDOUT:-true}
      # INIT_SEAFILE_ADMIN_EMAIL: ${INIT_SEAFILE_ADMIN_EMAIL:-admin@example.com}
      # INIT_SEAFILE_ADMIN_PASSWORD: ${INIT_SEAFILE_ADMIN_PASSWORD:-asecret}
      SEAFILE_SERVER_HOSTNAME: ${SEAFILE_SERVER_HOSTNAME}
      SEAFILE_SERVER_PROTOCOL: https
      JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY}
      ENABLE_SEADOC: true
      SEADOC_SERVER_URL: https://${SEAFILE_SERVER_HOSTNAME}/sdoc-server
      CACHE_PROVIDER: redis
      REDIS_HOST: redis
      REDIS_PORT: 6379
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - seafile_net
      - caddy_seafile_net

  seadoc:
    image: seafileltd/sdoc-server:2.0-latest
    restart: unless-stopped
    volumes:
      - ${SEAFILE_PATH}/docs:/shared
    environment:
      DB_HOST: db
      DB_PORT: 3306
      DB_USER: seafile
      DB_PASSWORD: ${SEAFILE_MYSQL_DB_PASSWORD}
      DB_NAME: seahub_db
      TIME_ZONE: ${TIME_ZONE:-Etc/UTC}
      JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY}
      SEAHUB_SERVICE_URL: http://seafile
    depends_on:
      db:
        condition: service_healthy
    networks:
      - seafile_net
      - caddy_seafile_net

  notification-server:
    image: seafileltd/notification-server:13.0-latest
    restart: unless-stopped
    volumes:
      - ${SEAFILE_PATH}/logs:/shared/logs
    environment:
      SEAFILE_MYSQL_DB_HOST: db
      SEAFILE_MYSQL_DB_PORT: 3306
      SEAFILE_MYSQL_DB_USER: seafile
      SEAFILE_MYSQL_DB_PASSWORD: ${SEAFILE_MYSQL_DB_PASSWORD}
      SEAFILE_MYSQL_DB_CCNET_DB_NAME: ccnet_db
      SEAFILE_MYSQL_DB_SEAFILE_DB_NAME: seafile_db
      JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY}
      SEAFILE_LOG_TO_STDOUT: ${SEAFILE_LOG_TO_STDOUT:-true}
      NOTIFICATION_SERVER_LOG_LEVEL: ${NOTIFICATION_SERVER_LOG_LEVEL:-info}
    depends_on:
      db:
        condition: service_healthy
    networks:
      - seafile_net
      - caddy_seafile_net

  onlyoffice:
    image: onlyoffice/documentserver:latest
    restart: unless-stopped
    environment:
      #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}
    volumes:
      - ${DATA_PATH}/seafile/onlyoffice/data:/var/www/onlyoffice/Data
      - ${DATA_PATH}/seafile/onlyoffice/logs:/var/log/onlyoffice
      - ${DATA_PATH}/seafile/onlyoffice/lib:/var/lib/onlyoffice
      - ${DATA_PATH}/seafile/onlyoffice/db:/var/lib/postgresql
      - ${DATA_PATH}/seafile/onlyoffice/redis:/var/lib/redis
      - ${DATA_PATH}/seafile/onlyoffice/rabbitmq:/var/lib/rabbitmq
      - ${DATA_PATH}/seafile/onlyoffice/fonts:/usr/share/fonts/truetype/custom
    networks:
      - caddy_seafile_net

  seafile-md-server:
    image: seafileltd/seafile-md-server:13.0-latest
    restart: unless-stopped
    volumes:
      # - ${SEAFILE_PATH}/metadata:/shared
      - ${SEAFILE_PATH}/files:/shared
    #ports:
      # - ${MD_PORT:-8084}:${MD_PORT:-8084}
    environment:
      JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY:?Variable is not set or empty}
      SEAFILE_MYSQL_DB_HOST: db
      SEAFILE_MYSQL_DB_PORT: 3306
      SEAFILE_MYSQL_DB_USER: seafile
      SEAFILE_MYSQL_DB_PASSWORD: ${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
      SEAFILE_MYSQL_DB_SEAFILE_DB_NAME: seafile_db
      SEAFILE_LOG_TO_STDOUT: ${SEAFILE_LOG_TO_STDOUT:-true}
      MD_PORT: ${MD_PORT:-8084}
      MD_LOG_LEVEL: ${MD_LOG_LEVEL:-info}
      MD_MAX_CACHE_SIZE: ${MD_MAX_CACHE_SIZE:-1GB}
      MD_CHECK_UPDATE_INTERVAL: ${MD_CHECK_UPDATE_INTERVAL:-30m}
      MD_FILE_COUNT_LIMIT: ${MD_FILE_COUNT_LIMIT:-100000}
      MD_STORAGE_TYPE: ${MD_STORAGE_TYPE:-disk}
      CACHE_PROVIDER: redis
      REDIS_HOST: redis
      REDIS_PORT: 6379
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    depends_on:
      db:
        condition: service_healthy
    networks:
      - seafile_net
      - caddy_seafile_net

networks:
  caddy_seafile_net:
    external: true
  seafile_net:
    internal: true

Question:
I have tried entering the container and running various scripts to upgrade the database (pro.py setup, etc.) but the arguments seem to be invalid for this version.

What is the correct and official procedure to upgrade the database schema when migrating from Community to Professional edition for version 13.0.11 in a Docker environment?

Thank you in advance for your help.

This is a bug.

You can manually create the missing field as following:

use ccnet_db;
ALTER TABLE EmailUser ADD COLUMN `is_department_owner` BOOL NOT NULL DEFAULT 0;
CREATE INDEX EmailUser_is_department_owenr on EmailUser(is_department_owner);

Thank you! That worked perfectly. I appreciate that effective solution.