# seafile — file sync service on ana-docker, fronted by Traefik at
# seafile.phasefinal.com.
#
# ⚠️ RESTART POLICY IS LOAD-BEARING HERE. None of these three services
# declared one until 2026-08-18, which means Docker defaulted them to `no`.
# On 2026-05-06T21:27:45Z the daemon stopped all three within 200ms of each
# other — a daemon restart or host reboot — and because the policy was `no`,
# nothing brought them back. Seafile then sat dead for three months and the
# only trace was an EXITED card on the dashboard that nobody read as an
# outage. The exit code was 255 on all three, which is just what a container
# that ignores SIGTERM reports when the daemon stops it; it is NOT evidence
# of a crash, and reading it as one sends you looking for a bug that does
# not exist. `unless-stopped` on all three is the fix.
#
# Data lives in two local named volumes (`seafile_seafile_db`,
# `seafile_seafile_datastore`) — NOT on ana-nas NFS, so this stack is not
# exposed to the NAS SPOF that affects postgres/rest-server/PBS.
services:
  db:
    image: mariadb:10.6
    container_name: seafile-mysql
    restart: unless-stopped
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PW}
      - MYSQL_LOG_CONSOLE=true
    volumes:
      - seafile_db:/var/lib/mysql # Requested, specifies the path to MySQL data persistent store.
    networks:
      - tnet
    # Required so seafile can wait for InnoDB to initialize before
    # starting seahub. Without it, after a daemon restart seahub races
    # mysql, hits "Connection refused", and the container's start.py
    # gives up — leaving nginx serving seafile but proxying to a dead
    # python backend. Image ships /usr/local/bin/healthcheck.sh.
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 5s
      timeout: 5s
      retries: 30
      start_period: 30s
  memcached:
    image: memcached:1.6.18
    container_name: seafile-memcached
    restart: unless-stopped
    entrypoint: memcached -m 256
    networks:
      - tnet
  seafile:
    image: seafileltd/seafile-mc:11.0-latest
    container_name: seafile
    restart: unless-stopped
    ports:
      - 9180:80
    volumes:
      - seafile_datastore:/shared # Requested, specifies the path to Seafile data persistent store.
    environment:
      - DB_HOST=db
      - DB_ROOT_PASSWD=${DB_ROOT_PW}
      - TIME_ZONE=America/Los_Angeles
      - SEAFILE_ADMIN_EMAIL=${SEAFILE_ADMIN_EMAIL}
      - SEAFILE_ADMIN_PASSWORD=${SEAFILE_ADMIN_PW}
    # Long-form depends_on with condition: service_healthy on db.
    # Compose waits for the mariadb healthcheck above to pass before
    # starting this container, so seahub never sees the
    # "Connection refused" race that wedges it on daemon restart.
    depends_on:
      db:
        condition: service_healthy
      memcached:
        condition: service_started
    labels:
      - homepage.group=Apps
      - homepage.name=SeaFile
      - homepage.icon=mdi-sync-circle
      - homepage.description=File Sync Service (ana)
      - homepage.href=https://seafile.phasefinal.com
      - traefik.enable=true
      - traefik.http.routers.seafile.tls=true
      - traefik.http.routers.seafile.rule=Host(`seafile.phasefinal.com`)
      - traefik.http.routers.seafile.tls.certresolver=anaprod
    networks:
      - tnet
    env_file:
      - .env
volumes:
  seafile_db: null
  seafile_datastore: null
networks:
  tnet:
    name: traefik-net
    external: true
