SeaTable deployment via Dokploy – License file turns into a directory

Description

We’re currently trying to deploy SeaTable Enterprise (v6.0.6) using Dokploy with Docker Compose, and we’ve been running into a persistent issue related to the license file initialization and the /shared volume mapping.

The deployment itself works up to the point where SeaTable starts and wants the license key to login — but at that moment, something goes wrong with how the license file is handled.

When the container is deployed without a pre-existing seatable-license.txt file, SeaTable automatically creates a directory named seatable-license.txt inside the shared and opt data paths.
From that point onward, it’s impossible to properly mount or overwrite the license file because Docker treats the existing directory as a conflict.

We have tried deploying SeaTable in several different ways, but each approach leads to the same issue — the seatable-license.txt either gets created as a directory or SeaTable fails to recognize it properly.

Case 1 – No license file existed before deployment
When SeaTable is deployed for the first time and no seatable-license.txt file is present, the system automatically creates a directory named seatable-license.txt inside the /shared/seatable path.
Once that happens, any later attempt to add or mount a proper license file fails, because Docker cannot overwrite a directory with a file.

Case 2 – License volume temporarily disabled (commented out)
We also tried commenting out the volume mount for the license file during the first build, so that we could manually create the license file and then reload the container.
However, after reloading, SeaTable expects a directory at that same path instead of the file, which results in a startup error because it cannot find the directory it’s looking for.

Case 3 – Using an Init container to create the license automatically
Our current setup uses an init container (seatable-init-license) that generates the seatable-license.txt during the deployment process.
Even in this setup, SeaTable either converts the file into a directory at startup or fails with an error related to the license path — the result is always the same: the license is not recognized and the container stops during initialization.
In all scenarios, the underlying issue seems to be tied to how SeaTable handles the /shared/seatable/seatable-license.txt path during its first run — it either assumes it should create the directory structure from scratch, or it overwrites an existing file with a directory placeholder.

Logs

seatable-init-license:
2025-10-31T16:12:48.712Z /sbin/my_init:25: SyntaxWarning: invalid escape sequence ‘\\W’
2025-10-31T16:12:48.712Z SHENV_NAME_WHITELIST_REGEX = re.compile(‘\\W’)
2025-10-31T16:12:48.713Z /sbin/my_init:96: SyntaxWarning: invalid escape sequence ‘\\Z’
2025-10-31T16:12:48.713Z value = re.sub(‘\\n\\Z’, ‘’, f.read())
2025-10-31T16:12:48.777Z \*\*\* Running /etc/my_init.d/01_init.sh…
2025-10-31T16:12:48.811Z \*\*\* Booting runit daemon…
2025-10-31T16:12:48.812Z \*\*\* Runit started as PID 16
2025-10-31T16:12:48.822Z 2025-10-31 17:12:48 Conf exists
2025-10-31T16:12:48.857Z 2025-10-31 17:12:48 Nginx ready
2025-10-31T16:12:48.874Z 2025-10-31 17:12:48 Updating CA certificates…
2025-10-31T16:12:55.390Z 2025-10-31 17:12:55 Start server
2025-10-31T16:12:48.812Z \*\*\* Running /templates/enterpoint.sh…
2025-10-31T16:12:55.397Z cat: /shared/seatable/seatable-license.txt: Is a directory
2025-10-31T16:12:55.402Z cat: /shared/seatable/seatable-license.txt: Is a directory
2025-10-31T16:12:59.017Z SeaTable started
2025-10-31T16:12:59.025Z 2025-10-31 17:12:59 For more startup information, please check the /opt/seatable/logs/init.log
2025-10-31T16:12:59.029Z 2025-10-31 17:12:59 This is an idle script (infinite loop) to keep the container running.

seatable-server:

2025-10-31T16:12:48.712Z /sbin/my_init:25: SyntaxWarning: invalid escape sequence ‘\\W’
2025-10-31T16:12:48.712Z SHENV_NAME_WHITELIST_REGEX = re.compile(‘\\W’)
2025-10-31T16:12:48.713Z /sbin/my_init:96: SyntaxWarning: invalid escape sequence ‘\\Z’
2025-10-31T16:12:48.713Z value = re.sub(‘\\n\\Z’, ‘’, f.read())
2025-10-31T16:12:48.777Z \*\*\* Running /etc/my_init.d/01_init.sh…
2025-10-31T16:12:48.811Z \*\*\* Booting runit daemon…
2025-10-31T16:12:48.812Z \*\*\* Runit started as PID 16
2025-10-31T16:12:48.812Z \*\*\* Running /templates/enterpoint.sh…
2025-10-31T16:12:48.822Z 2025-10-31 17:12:48 Conf exists
2025-10-31T16:12:48.857Z 2025-10-31 17:12:48 Nginx ready
2025-10-31T16:12:48.874Z 2025-10-31 17:12:48 Updating CA certificates…
2025-10-31T16:12:55.390Z 2025-10-31 17:12:55 Start server
2025-10-31T16:12:59.017Z SeaTable started
2025-10-31T16:12:59.025Z 2025-10-31 17:12:59 For more startup information, please check the /opt/seatable/logs/init.log
2025-10-31T16:12:59.029Z 2025-10-31 17:12:59 This is an idle script (infinite loop) to keep the container running.
2025-10-31T16:12:55.397Z cat: /shared/seatable/seatable-license.txt: Is a directory
2025-10-31T16:12:55.402Z cat: /shared/seatable/seatable-license.txt: Is a directory

Docker Compose:

services:
  # License file initializer - runs once to create the license file
  seatable-init-license:
    image: curlimages/curl:latest
    container_name: seatable-init-license
    restart: "no"
    volumes:
      - /opt/seatable
    environment:
      SEATABLE_LICENSE_URL: ${SEATABLE_LICENSE_URL:-}
      SEATABLE_LICENSE_CONTENT: ${SEATABLE_LICENSE_CONTENT:-}
    entrypoint: sh
    command: |
      -c '
        echo "[INFO] Initializing license file..."
        mkdir -p /shared/seatable

        if [ -f "/shared/seatable/seatable-license.txt" ]; then
          echo "[INFO] License file already exists"
        elif [ -n "$${SEATABLE_LICENSE_URL}" ]; then
          echo "[INFO] Downloading license file from URL..."
          if curl -f -L -o /shared/seatable/seatable-license.txt "$${SEATABLE_LICENSE_URL}"; then
            echo "[INFO] License file downloaded successfully"
          else
            echo "[ERROR] Failed to download license from URL: $${SEATABLE_LICENSE_URL}"
            exit 1
          fi
        elif [ -n "$${SEATABLE_LICENSE_CONTENT}" ]; then
          echo "[INFO] Creating license file from environment variable..."
          echo "$${SEATABLE_LICENSE_CONTENT}" > /shared/seatable/seatable-license.txt
          echo "[INFO] License file created from content"
        else
          echo "[INFO] Creating placeholder license file..."
          cat > /shared/seatable/seatable-license.txt << "EOF"
      # SeaTable License File
      # Please add your license content here
      # You can either:
      # 1. Set the SEATABLE_LICENSE_URL environment variable with your license download URL
      # 2. Set the SEATABLE_LICENSE_CONTENT environment variable with the license content
      # 3. Edit this file directly after deployment
      # 4. Use docker exec to add your license content
      EOF
          echo "[INFO] Placeholder license file created"
        fi

        chmod 644 /shared/seatable/seatable-license.txt
        echo "[INFO] License initialization complete"
      '

  seatable-server:
    image: seatable/seatable-enterprise:6.0.6
    container_name: seatable-server
    restart: unless-stopped
    depends_on:
      seatable-init-license:
        condition: service_completed_successfully
      mariadb:
        condition: service_healthy
      redis:
        condition: service_healthy
    environment:
      # DB / Redis
      SEATABLE_MYSQL_DB_HOST: ${MARIADB_HOST:-mariadb}
      SEATABLE_MYSQL_DB_PORT: ${MARIADB_PORT:-3306}
      SEATABLE_MYSQL_DB_USER: root
      SEATABLE_MYSQL_DB_PASSWORD: ${MARIADB_PASSWORD}
      INIT_SEATABLE_MYSQL_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD:-${MARIADB_PASSWORD}}
      SEATABLE_MYSQL_DB_DTABLE_DB_NAME: dtable_db
      SEATABLE_MYSQL_DB_CCNET_DB_NAME: ccnet_db
      SEATABLE_MYSQL_DB_SEAFILE_DB_NAME: seafile_db
      REDIS_HOST: ${REDIS_HOST:-redis}
      REDIS_PORT: ${REDIS_PORT:-6379}
      REDIS_PASSWORD: ${REDIS_PASSWORD}

      # Server
      SEATABLE_SERVER_HOSTNAME: ${SEATABLE_SERVER_HOSTNAME}
      SEATABLE_SERVER_PROTOCOL: http
      SEATABLE_ADMIN_EMAIL: ${SEATABLE_ADMIN_EMAIL}
      SEATABLE_ADMIN_PASSWORD: ${SEATABLE_ADMIN_PASSWORD}
      TIME_ZONE: ${TIME_ZONE:-Europe/Berlin}
      JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY}

      # Optional
      ENABLE_PYTHON_SCRIPT: "false"
      SEATABLE_LOG_LEVEL: ERROR

    volumes:
      - /opt/seatable:/shared

    # Traefik configuration
    labels:
      - traefik.enable=true
      - traefik.http.routers.seatable.rule=Host(`${SEATABLE_SERVER_HOSTNAME}`)
      - traefik.http.routers.seatable.entrypoints=websecure
      - traefik.http.routers.seatable.tls=true
      - traefik.http.services.seatable.loadbalancer.server.port=80
      - traefik.docker.network=dokploy-network

    networks:
      - dokploy-network
      - backend-seatable-net

    healthcheck:
      test: ["CMD-SHELL", "curl --fail http://localhost:8000 || exit 1"]
      interval: 20s
      retries: 3
      start_period: 30s
      timeout: 10s

  mariadb:
    image: mariadb:11.8.3-noble
    container_name: mariadb
    restart: unless-stopped
    command: ["mariadbd","--innodb_snapshot_isolation=OFF"]
    environment:
      MYSQL_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD:-${MARIADB_PASSWORD}}
      MYSQL_LOG_CONSOLE: "true"
      MARIADB_AUTO_UPGRADE: "1"
      TZ: ${TIME_ZONE:-Europe/Berlin}
    volumes:
      - mariadb-data:/var/lib/mysql
    healthcheck:
      test: ["CMD","/usr/local/bin/healthcheck.sh","--connect","--mariadbupgrade","--innodb_initialized"]
      interval: 20s
      retries: 3
      start_period: 30s
      timeout: 10s
    networks:
      - backend-seatable-net

  redis:
    image: redis:8.2.2-bookworm
    container_name: redis
    restart: unless-stopped
    environment:
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    command: >
      /bin/sh -c 'redis-server --requirepass "${REDIS_PASSWORD}"'
    healthcheck:
      test: ["CMD","redis-cli","ping"]
      interval: 20s
      retries: 3
      timeout: 5s
    networks:
      - backend-seatable-net

volumes:
  seatable-data:
    name: seatable-data
  mariadb-data:
    name: mariadb-data

networks:
  dokploy-network:
    external: true
  backend-seatable-net:
    name: backend-seatable-net
1 Like

Why don’t you use the bind mount as described in the official seatable-server.yml?

---
services:
  seatable-server:
    image: ${SEATABLE_IMAGE:-seatable/seatable-enterprise:6.0.10}
    restart: unless-stopped
    container_name: seatable-server
    volumes:
      - "/opt/seatable-server:/shared"
      - ./config/seatable-nginx.conf:/etc/nginx/sites-enabled/default
      - type: bind
        source: "./seatable-license.txt"
        target: "/shared/seatable/seatable-license.txt"
        read_only: ${SEATABLE_LICENSE_FORCE_READ_ONLY:-false}
  ...

This will prevent, that a directory with the name seatable-license.txt is created.
It doesn’t matter if you use dokploy or docker compose to initialize seatable server.

Why did you removed the bind mount from your setup?

Das haben wir selbstverständlich bereits ausprobiert. Wir haben rund sechs Stunden verschiedene Konfigurationen getestet und sind schließlich bei der oben geposteten Version gelandet.

Ich kann gerne die Variante mit dem Bind-Mount hier posten, wo zumindest die Login Seite lädt. Das Problem ist hier das wenn man nachschaut auch direkt eine Directory erstellt wird.

Wenn ich mich versuche einzuloggen kommt folgender Fehler:

# Page unavailable

Sorry, but the requested page is unavailable due to a server hiccup.

Our engineers have been notified, so check back later.

Wichtig ist zudem, dass Dokploy mit Traefik arbeitet und nicht mit Caddy. Entsprechend ist die Compose-Datei darauf angepasst – andernfalls treten Fehler im Setup auf.

Das hier ist der Log vom seatable-server, wenn es ganz frisch deployed wurde.

2025-11-04T11:31:48.382Z 2025-11-04 12:31:48 Conf exists
2025-11-04T11:31:48.419Z 2025-11-04 12:31:48 Nginx ready
2025-11-04T11:31:48.436Z 2025-11-04 12:31:48 Updating CA certificates...
2025-11-04T11:31:54.895Z 2025-11-04 12:31:54 Start server
2025-11-04T11:31:58.515Z SeaTable started
2025-11-04T11:31:58.520Z 2025-11-04 12:31:58 For more startup information, please check the /opt/seatable/logs/init.log
2025-11-04T11:31:58.522Z 2025-11-04 12:31:58 This is an idle script (infinite loop) to keep the container running.
2025-11-04T11:31:48.278Z /sbin/my_init:25: SyntaxWarning: invalid escape sequence '\W'
2025-11-04T11:31:48.279Z SHENV_NAME_WHITELIST_REGEX = re.compile('\W')
2025-11-04T11:31:48.279Z /sbin/my_init:96: SyntaxWarning: invalid escape sequence '\Z'
2025-11-04T11:31:48.279Z value = re.sub('\n\Z', '', f.read())
2025-11-04T11:31:48.339Z *** Running /etc/my_init.d/01_init.sh...
2025-11-04T11:31:48.371Z *** Booting runit daemon...
2025-11-04T11:31:48.372Z *** Runit started as PID 16
2025-11-04T11:31:48.372Z *** Running /templates/enterpoint.sh...
2025-11-04T11:31:54.902Z cat: /shared/seatable/seatable-license.txt: Is a directory
2025-11-04T11:31:54.907Z cat: /shared/seatable/seatable-license.txt: Is a directory

Hier ist der Log von MariaDB

2025-11-04T11:31:27.143Z 2025-11-04 12:31:27+01:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.8.3+maria~ubu2404 started.
2025-11-04T11:31:27.497Z 2025-11-04 12:31:27+01:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2025-11-04T11:31:27.510Z 2025-11-04 12:31:27+01:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.8.3+maria~ubu2404 started.
2025-11-04T11:31:27.903Z 2025-11-04 12:31:27+01:00 [Note] [Entrypoint]: MariaDB upgrade not required
2025-11-04T11:31:27.490Z 2025-11-04 12:31:27+01:00 [Warn] [Entrypoint]: /sys/fs/cgroup///memory.pressure not writable, functionality unavailable to MariaDB
2025-11-04T11:31:27.949Z 2025-11-04 12:31:27 0 [Note] Starting MariaDB 11.8.3-MariaDB-ubu2404 source revision b565b3e7e041c480fd8013e3beec6b99544d6bf8 server_uid r4sVOlN7jnRVUb1r7CByn6EHZJw= as process 1
2025-11-04T11:31:27.982Z 2025-11-04 12:31:27 0 [Note] InnoDB: Compressed tables use zlib 1.3
2025-11-04T11:31:27.986Z 2025-11-04 12:31:27 0 [Note] InnoDB: Number of transaction pools: 1
2025-11-04T11:31:27.986Z 2025-11-04 12:31:27 0 [Note] InnoDB: Using AVX512 instructions
2025-11-04T11:31:27.986Z 2025-11-04 12:31:27 0 [Note] mariadbd: O_TMPFILE is not supported on /tmp (disabling future attempts)
2025-11-04T11:31:27.988Z 2025-11-04 12:31:27 0 [Warning] mariadbd: io_uring_queue_init() failed with EPERM: sysctl kernel.io_uring_disabled has the value 2, or 1 and the user of the process is not a member of sysctl kernel.io_uring_group. (see man 2 io_uring_setup).
2025-11-04T11:31:27.988Z create_uring failed: falling back to libaio
2025-11-04T11:31:27.988Z 2025-11-04 12:31:27 0 [Note] InnoDB: Using Linux native AIO
2025-11-04T11:31:27.989Z 2025-11-04 12:31:27 0 [Note] InnoDB: innodb_buffer_pool_size_max=128m, innodb_buffer_pool_size=128m
2025-11-04T11:31:27.991Z 2025-11-04 12:31:27 0 [Note] InnoDB: Completed initialization of buffer pool
2025-11-04T11:31:27.992Z 2025-11-04 12:31:27 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
2025-11-04T11:31:28.014Z 2025-11-04 12:31:28 0 [Note] InnoDB: End of log at LSN=1579890
2025-11-04T11:31:28.062Z 2025-11-04 12:31:28 0 [Note] InnoDB: Opened 3 undo tablespaces
2025-11-04T11:31:28.062Z 2025-11-04 12:31:28 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
2025-11-04T11:31:28.064Z 2025-11-04 12:31:28 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
2025-11-04T11:31:28.064Z 2025-11-04 12:31:28 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
2025-11-04T11:31:28.067Z 2025-11-04 12:31:28 0 [Note] InnoDB: log sequence number 1579890; transaction id 1067
2025-11-04T11:31:28.068Z 2025-11-04 12:31:28 0 [Note] Plugin 'FEEDBACK' is disabled.
2025-11-04T11:31:28.068Z 2025-11-04 12:31:28 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
2025-11-04T11:31:28.068Z 2025-11-04 12:31:28 0 [Note] Plugin 'wsrep-provider' is disabled.
2025-11-04T11:31:28.194Z 2025-11-04 12:31:28 0 [Note] InnoDB: Buffer pool(s) load completed at 251104 12:31:28
2025-11-04T11:31:31.202Z 2025-11-04 12:31:31 0 [Note] Server socket created on IP: '0.0.0.0', port: '3306'.
2025-11-04T11:31:31.202Z 2025-11-04 12:31:31 0 [Note] Server socket created on IP: '::', port: '3306'.
2025-11-04T11:31:31.228Z 2025-11-04 12:31:31 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
2025-11-04T11:31:31.229Z 2025-11-04 12:31:31 0 [Note] mariadbd: ready for connections.
2025-11-04T11:31:31.229Z Version: '11.8.3-MariaDB-ubu2404'  socket: '/run/mysqld/mysqld.sock'  port: 3306  mariadb.org binary distribution

Hier ist der Log von redis

2025-11-04T11:31:27.157Z 10:C 04 Nov 2025 11:31:27.156 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
2025-11-04T11:31:27.157Z 10:C 04 Nov 2025 11:31:27.156 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
2025-11-04T11:31:27.157Z 10:C 04 Nov 2025 11:31:27.156 * Redis version=8.2.2, bits=64, commit=00000000, modified=1, pid=10, just started
2025-11-04T11:31:27.157Z 10:C 04 Nov 2025 11:31:27.156 * Configuration loaded
2025-11-04T11:31:27.157Z 10:M 04 Nov 2025 11:31:27.157 * monotonic clock: POSIX clock_gettime
2025-11-04T11:31:27.158Z 10:M 04 Nov 2025 11:31:27.158 * Running mode=standalone, port=6379.
2025-11-04T11:31:27.159Z 10:M 04 Nov 2025 11:31:27.159 * Server initialized
2025-11-04T11:31:27.159Z 10:M 04 Nov 2025 11:31:27.159 * Ready to accept connections tcp

Das hier ist die Compose

services:
  seatable-server:
    image: seatable/seatable-enterprise:6.0.6
    container_name: seatable-server
    restart: unless-stopped
    depends_on:
      mariadb:
        condition: service_healthy
      redis:
        condition: service_healthy
    environment:
      # DB / Redis
      SEATABLE_MYSQL_DB_HOST: ${MARIADB_HOST:-mariadb}
      SEATABLE_MYSQL_DB_PORT: ${MARIADB_PORT:-3306}
      SEATABLE_MYSQL_DB_USER: root
      SEATABLE_MYSQL_DB_PASSWORD: ${MARIADB_PASSWORD}
      INIT_SEATABLE_MYSQL_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD:-${MARIADB_PASSWORD}}
      SEATABLE_MYSQL_DB_DTABLE_DB_NAME: dtable_db
      SEATABLE_MYSQL_DB_CCNET_DB_NAME: ccnet_db
      SEATABLE_MYSQL_DB_SEAFILE_DB_NAME: seafile_db
      REDIS_HOST: ${REDIS_HOST:-redis}
      REDIS_PORT: ${REDIS_PORT:-6379}
      REDIS_PASSWORD: ${REDIS_PASSWORD}

      # Server
      SEATABLE_SERVER_HOSTNAME: ${SEATABLE_SERVER_HOSTNAME}   # z.B. seatable.example.com
      SEATABLE_SERVER_PROTOCOL: https
      SEATABLE_ADMIN_EMAIL: ${SEATABLE_ADMIN_EMAIL}
      SEATABLE_ADMIN_PASSWORD: ${SEATABLE_ADMIN_PASSWORD}
      TIME_ZONE: ${TIME_ZONE:-Europe/Berlin}
      JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY}

      # Optional erstmal aus:
      ENABLE_PYTHON_SCRIPT: "false"     # Später auf true + Python Scheduler hinzufügen
      SEATABLE_LOG_LEVEL: ERROR

    volumes:
      - "/opt/seatable-server:/shared"
      - type: bind
        source: "./seatable-license.txt"
        target: "/shared/seatable/seatable-license.txt"
        read_only: ${SEATABLE_LICENSE_FORCE_READ_ONLY:-false}

    # Traefik statt Caddy
    labels:
      - traefik.enable=true
      - traefik.http.routers.seatable.rule=Host(`${SEATABLE_SERVER_HOSTNAME}`)
      - traefik.http.routers.seatable.entrypoints=websecure
      - traefik.http.routers.seatable.tls=true
      - traefik.http.services.seatable.loadbalancer.server.port=80
      - traefik.docker.network=dokploy-network

    networks:
      - dokploy-network
      - backend-seatable-net

    healthcheck:
      test: ["CMD-SHELL", "curl --fail http://localhost:8000 || exit 1"]
      interval: 20s
      retries: 3
      start_period: 30s
      timeout: 10s

  mariadb:
    image: mariadb:11.8.3-noble
    container_name: mariadb
    restart: unless-stopped
    command: ["mariadbd","--innodb_snapshot_isolation=OFF"]
    environment:
      MYSQL_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD:-${MARIADB_PASSWORD}}
      MYSQL_LOG_CONSOLE: "true"
      MARIADB_AUTO_UPGRADE: "1"
      TZ: ${TIME_ZONE:-Europe/Berlin}
    volumes:
      - /opt/mariadb:/var/lib/mysql
    healthcheck:
      test: ["CMD","/usr/local/bin/healthcheck.sh","--connect","--mariadbupgrade","--innodb_initialized"]
      interval: 20s
      retries: 3
      start_period: 30s
      timeout: 10s
    networks:
      - backend-seatable-net

  redis:
    image: redis:8.2.2-bookworm
    container_name: redis
    restart: unless-stopped
    environment:
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    command: >
      /bin/sh -c 'redis-server --requirepass "$${REDIS_PASSWORD}"'
    healthcheck:
      test: ["CMD","redis-cli","ping"]
      interval: 20s
      retries: 3
      timeout: 5s
    networks:
      - backend-seatable-net

networks:
  dokploy-network:
    external: true
  backend-seatable-net:
    name: backend-seatable-net

Das Volume lasse ich bewusst raus, da es sonst zum Fehler “Bad Gateway” kommt
- ./config/seatable-nginx.conf:/etc/nginx/sites-enabled/default

Hast du das Volume von SeaTable-Server oder das Verzeichnis seatable-license.txt gelöscht? Wahrscheinlich nicht!

Folgendes ist passiert:

Bei deinem ersten Start hattest du noch den mount von seatable-license.txt in deiner yml, aber keine entsprechende Datei an der Stelle gespeichert. Deshalb hat SeaTable ein entsprechendes Verzeichnis in /shared/... angelegt.

Da /shared im Volume liegt, bleibt dieses Verzeichnis, welches du nicht haben willst, auch nach einem Neustart der Container im Host bestehen.

Deshalb kommt es zu dem Fehler: 2025-11-04T11:31:54.902Z cat: /shared/seatable/seatable-license.txt: Is a directory.

Somit:

Lösch das Volume oder das Verzeichnis und starte die Container neu.

Danke für die schnelle Rückmeldung.

Ich habe den Server jetzt mal komplett auf 0 gesetzt, dementsprechend kann kein Volumen mehr vorhanden sein.

Deployment hat einwandfrei funktioniert.

Ich habe die Docker Compose jetzt mal so angepasst, dass es fast 1:1 so ist wie die seatable-server.yml, nur sind die labels auf traefik angepasst:

services:
  seatable-server:
    image: seatable/seatable-enterprise:6.0.10
    restart: unless-stopped
    container_name: seatable-server
    volumes:
      - "/opt/seatable-server:/shared"
      - type: bind
        source: "./seatable-license.txt"
        target: "/shared/seatable/seatable-license.txt"
        read_only: ${SEATABLE_LICENSE_FORCE_READ_ONLY:-false}

    environment:
      # DB / Redis
      - SEATABLE_MYSQL_DB_HOST=${MARIADB_HOST:-mariadb}
      - SEATABLE_MYSQL_DB_PORT=${MARIADB_PORT:-3306}
      - SEATABLE_MYSQL_DB_USER=root
      - SEATABLE_MYSQL_DB_PASSWORD=${MARIADB_PASSWORD:?Variable is not set or empty}
      - INIT_SEATABLE_MYSQL_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD:-${MARIADB_PASSWORD}}
      - SEATABLE_MYSQL_DB_DTABLE_DB_NAME=dtable_db
      - SEATABLE_MYSQL_DB_CCNET_DB_NAME=ccnet_db
      - SEATABLE_MYSQL_DB_SEAFILE_DB_NAME=seafile_db
      - REDIS_HOST=${REDIS_HOST:-redis}
      - REDIS_PORT=${REDIS_PORT:-6379}
      - REDIS_PASSWORD=${REDIS_PASSWORD:?Variable is not set or empty}

      #Server
      - SEATABLE_SERVER_HOSTNAME=${SEATABLE_SERVER_HOSTNAME:?Variable is not set or empty}
      - SEATABLE_SERVER_PROTOCOL=${SEATABLE_SERVER_PROTOCOL:-https}
      - SEATABLE_ADMIN_EMAIL=${SEATABLE_ADMIN_EMAIL:?Variable is not set or empty}
      - SEATABLE_ADMIN_PASSWORD=${SEATABLE_ADMIN_PASSWORD:?Variable is not set or empty}
      - TIME_ZONE=${TIME_ZONE:-Europe/Berlin}
      - JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}

      - ENABLE_PYTHON_SCRIPT=${ENABLE_PYTHON_SCRIPT:-true}
      - PYTHON_SCHEDULER_URL=${PYTHON_SCHEDULER_URL:-http://python-scheduler}
      - PYTHON_SCHEDULER_AUTH_TOKEN=${PYTHON_SCHEDULER_AUTH_TOKEN:-}
      - REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
      - SEATABLE_EMAIL_USE_TLS=${SEATABLE_EMAIL_USE_TLS:-}
      - SEATABLE_EMAIL_HOST=${SEATABLE_EMAIL_HOST:-}
      - SEATABLE_EMAIL_HOST_USER=${SEATABLE_EMAIL_HOST_USER:-}
      - SEATABLE_EMAIL_HOST_PASSWORD=${SEATABLE_EMAIL_HOST_PASSWORD:-}
      - SEATABLE_EMAIL_PORT=${SEATABLE_EMAIL_PORT:-}
      - SEATABLE_DEFAULT_FROM_EMAIL=${SEATABLE_DEFAULT_FROM_EMAIL:-}
      - SEATABLE_SERVER_EMAIL=${SEATABLE_SERVER_EMAIL:-}
      - SEATABLE_SHOW_TEMPLATES_LINK=${SEATABLE_SHOW_TEMPLATES_LINK:-}
      - SEATABLE_TEMPLATE_BASE_API_TOKEN=${SEATABLE_TEMPLATE_BASE_API_TOKEN:-}
      - SEATABLE_TEMPLATE_TABLE_NAME=${SEATABLE_TEMPLATE_TABLE_NAME:-}
      - SEATABLE_ENABLE_CREATE_BASE_FROM_TEMPLATE=${SEATABLE_ENABLE_CREATE_BASE_FROM_TEMPLATE:-}
      - SEATABLE_HELP_LINK=${SEATABLE_HELP_LINK:-https://help.seatable.com}
      - SEATABLE_LOG_LEVEL=${SEATABLE_LOG_LEVEL:-INFO}
      - ENABLE_SEATABLE_AI=${ENABLE_SEATABLE_AI:-false}
      - SEATABLE_AI_SERVER_URL=${SEATABLE_AI_SERVER_URL:-http://seatable-ai:8888}

    labels:
      - traefik.enable=true
      - traefik.http.routers.seatable.rule=Host(`${SEATABLE_SERVER_HOSTNAME}`)
      - traefik.http.routers.seatable.entrypoints=websecure
      - traefik.http.routers.seatable.tls=true
      - traefik.http.services.seatable.loadbalancer.server.port=80
      - traefik.docker.network=dokploy-network

    depends_on:
      mariadb:
        condition: service_healthy
      redis:
        condition: service_healthy

    networks:
      - dokploy-network
      - backend-seatable-net

    healthcheck:
      test: ["CMD-SHELL", "curl --fail http://localhost:8000 || exit 1"]
      interval: 20s
      retries: 3
      start_period: 30s
      timeout: 10s

  mariadb:
    image: ${SEATABLE_DB_IMAGE:-mariadb:11.8.3-noble}
    restart: unless-stopped
    container_name: mariadb
    command: ["mariadbd", "--innodb_snapshot_isolation=OFF"]
    environment:
      - MYSQL_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD:-${MARIADB_PASSWORD}}
      - MYSQL_LOG_CONSOLE=true
      - MARIADB_AUTO_UPGRADE=1
      - TZ=${TIME_ZONE}
    volumes:
      - "/opt/mariadb:/var/lib/mysql"
      #- "/opt/seatable-compose/mariadb-custom.cnf:/etc/mysql/conf.d/99-mariadb-custom.cnf"
    networks:
      - backend-seatable-net
    healthcheck:
      test:
        [
          "CMD",
          "/usr/local/bin/healthcheck.sh",
          "--connect",
          "--mariadbupgrade",
          "--innodb_initialized",
        ]
      interval: 20s
      retries: 3
      start_period: 30s
      timeout: 10s

  redis:
    image: ${SEATABLE_REDIS_IMAGE:-redis:8.2.2-bookworm}
    restart: unless-stopped
    container_name: redis
    environment:
      - REDIS_PASSWORD=${REDIS_PASSWORD:?Variable is not set or empty}
    command:
      - /bin/sh
      - -c
      - redis-server --requirepass "$${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}"
    networks:
      - backend-seatable-net
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 20s
      retries: 3
      timeout: 5s

networks:
  dokploy-network:
    external: true
  backend-seatable-net:
    name: backend-seatable-net

Direkt nach dem Deploy habe ich im Terminal geschaut und versucht die Lizenz zu hinterlegen

root@:/# cd opt/seatable
root@:/opt/seatable# ls
ccnet    logs     seafile-data          seatable-server-6.0.10
conf     pids     seahub-data           seatable-server-latest
db-data  scripts  seatable-license.txt  storage-data
curl https://get.seatable.com/license/download/56c802d23f948f96fb65c05f2ffcd165 > ./seatable-license.txt2ffcd165 > ./seatable-license
bash: ./seatable-license.txt: Is a directory

Leider kommt trotzdem direkt der fehler. Was kann ich noch versuchen?

Problem 1

Deine seatable-license.txt muss im gleichen Ordner wie deine docker-compose.yml liegen - und zwar bevor du den Container zum ersten Mal startest. Wenn die Datei da nicht liegt, wird im Container keine Datei gemountet sondern ein gleichnamiger Ordner angelegt und genau das siehst du ja als Problem in deinen Logs.

Problem 2

Schau dir mal diesen Befehl an. Da stimmt doch was nicht. Kommt der so aus der E-Mail von uns? Ich bezweifle es.

Ich habe die Befehle umgebrochen, damit du sie besser vergleichen kannst!

curl https://get.seatable.com/license/download/56c802d23f948f96fb65c05f2ffcd165 
> ./seatable-license.txt2ffcd165 > ./seatable-license

Der Befehl zum herunterladen der Lizenz und abspeichern im gleichen Verzeichnis ist einfach nur so:

curl https://get.seatable.com/license/download/56c802d23f948f96fb65c05f2ffcd165 
> ./seatable-license.txt

Anhand deiner Problem gehe ich davon aus, dass du bisher wenig Erfahrung mit Docker und Docker Compose hast. Auch Dokploy wird nicht vor der technischen Komplexität schützen, die der Betrieb eines eines Servers bedeutet. Vielleicht ist SeaTable Cloud das bessere Produkt für dich.

Gruß
Christoph

Could you solve your problem and install SeaTable with dokploy?
Please confirm and then close the topic.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.