Speckle server not starting(or is it up-ing) correctly

Hi Specklers

We’re having a bit of trouble with our digital ocean hosted speckler server, its been an issue since we upgraded from v2.25.7 to v2.25.9

We’re using docker compose (see our compose file below), and it seems like the health check fails for the server so it never starts the webhook-, preview- or fileimport-services. The server is accessible but the IFC-upload process will fail immediately.

When I inspect the server container using
docker inspect speckle-server-speckle-server-1 --format '{{json .State.Health}}' | jq
I get a bunch of these:

  "Status": "unhealthy",
  "FailingStreak": 65,
  "Log": [
    {
      "Start": "2025-08-25T13:36:57.399315328Z",
      "End": "2025-08-25T13:36:57.493749373Z",
      "ExitCode": 1,
      "Output": "node:internal/errors:540\n      throw error;\n      ^\n\nTypeError [ERR_INVALID_ARG_TYPE]: The \"code\" argument must be of type number. Received type boolean (false)\n    at process.set [as exitCode] (node:internal/bootstrap/node:122:9)\n    at process.exit (node:internal/process/per_thread:189:24)\n    at IncomingMessage.<anonymous> ([eval]:1:264)\n    at IncomingMessage.emit (node:events:530:35)\n    at endReadableNT (node:internal/streams/readable:1698:12)\n    at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {\n  code: 'ERR_INVALID_ARG_TYPE'\n}\n\nNode.js v22.17.0\n"
    },

Hope someone can help us in the right direction. As mentioned it was working flawlessly in v2.25.7

Our compose file looks like this (we’re using an env-file to make updating+switching to our dev-instance easier and its based on the previous guide from the documentation and the healtcheck from here speckle-server/docker-compose-speckle.yml at ba8a62dd2aade741346e431f74812eddfc54c020 · specklesystems/speckle-server · GitHub) :


name: "speckle-server"

services:

  # Reverse proxy needed to make sure that we get a certificat
  reverse-proxy:
    image: traefik:v2.10
    restart: always
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      # To use Let's Encrypt staging server instead of production, uncomment the following line
      #- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
      # TODO: replace `{[email protected]}` with your actual email
      - "[email protected]"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
      # To enable the Traefik web UI (enabled by --api.insecure=true); this is not recommended as it will expose the Traefik dashboard to the internet
      #- "--api.insecure=true"

    ports:
      # The HTTPS port (required for Traefik to listen to HTTPS requests)
      - "443:443"
      # The Traefik Web UI port if enabled by --api.insecure=true
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      # So that Traefik can listen to the Docker events
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  ####
  # Speckle Server dependencies
  #######
  postgres:
    image: "postgres:16.9-alpine"
    restart: always
    environment:
      POSTGRES_DB: speckle
      POSTGRES_USER: speckle
      POSTGRES_PASSWORD: speckle
    volumes:
      - ./postgres-data:/var/lib/postgresql/data/
    healthcheck:
      # the -U user has to match the POSTGRES_USER value
      test: ["CMD-SHELL", "pg_isready -U speckle"]
      interval: 5s
      timeout: 5s
      retries: 30

  redis:
    image: "valkey/valkey:8-alpine"
    restart: always
    volumes:
      - ./redis-data:/data
    ports:
      - "127.0.0.1:6379:6379"
    healthcheck:
      test: ["CMD", "valkey-cli", "--raw", "incr", "ping"]
      interval: 5s
      timeout: 5s
      retries: 30

  minio:
    image: "minio/minio"
    command: server /data --console-address ":9001"
    restart: always
    volumes:
      - ./minio-data:/data
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 5s
      timeout: 5s
      retries: 5

  ####
  # Speckle Server
  #######
  speckle-ingress:
    image: speckle/speckle-docker-compose-ingress:${SPECKLE_VERSION}
    restart: always
    ports: []
      #- "0.0.0.0:80:8080"
    environment:
      FILE_SIZE_LIMIT_MB: "100"
      NGINX_ENVSUBST_OUTPUT_DIR: "/etc/nginx"
    labels:
    - "traefik.enable=true"
    #TODO: replace `example.com` with your domain. This should just be the domain, and do not include the protocol (http/https).
    - "traefik.http.routers.speckle-ingress.rule=Host(`${REVERSE_URL}`)"
    - "traefik.http.routers.speckle-ingress.entrypoints=websecure"
    - "traefik.http.routers.speckle-ingress.tls.certresolver=myresolver"
    - "traefik.http.services.speckle-ingress.loadbalancer.server.port=8080"

  speckle-frontend-2:
    image: speckle/speckle-frontend-2:${SPECKLE_VERSION}
    restart: always
    environment:
      NUXT_PUBLIC_SERVER_NAME: "local"
      # TODO: Change NUXT_PUBLIC_API_ORIGIN to the URL of the speckle server, as accessed from the network. This is the same value as should be used for the CANONICAL_URL in the server section below.
      NUXT_PUBLIC_API_ORIGIN: "${PUBLIC_URL}"
      NUXT_PUBLIC_BACKEND_API_ORIGIN: "http://speckle-server:3000"
      # TODO: Change NUXT_PUBLIC_BASE_URL to the URL of the speckle frontend, as accessed from the network. This is the same value as should be used for the CANONICAL_URL in the server section below.
      NUXT_PUBLIC_BASE_URL: "${PUBLIC_URL}"
      NUXT_PUBLIC_LOG_LEVEL: 'warn'
      NUXT_REDIS_URL: "redis://redis"

  speckle-server:
    image: speckle/speckle-server:${SPECKLE_VERSION}
    restart: always
    healthcheck:
      test:
        - CMD
        - /nodejs/bin/node
        - -e
        - "try { require('node:http').request({headers: {'Content-Type': 'application/json'}, port:3000, hostname:'127.0.0.1', path:'/readiness', method: 'GET', timeout: 2000 }, (res) => { body = ''; res.on('data', (chunk) => {body += chunk;}); res.on('end', () => {process.exit(res.statusCode != 200 || body.toLowerCase().includes('error'));}); }).end(); } catch { process.exit(1); }"
      interval: 10s
      timeout: 10s
      retries: 3
      start_period: 90s
      
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      minio:
        condition: service_healthy
    environment:
      # TODO: Change this to the URL of the speckle server, as accessed from the network
      CANONICAL_URL: "${PUBLIC_URL}"
      SPECKLE_AUTOMATE_URL: "http://127.0.0.1:3030"

      REDIS_URL: "redis://redis"

      S3_ENDPOINT: "http://minio:9000"
      S3_ACCESS_KEY: "minioadmin"
      S3_SECRET_KEY: "minioadmin"
      S3_BUCKET: "speckle-server"
      S3_CREATE_BUCKET: "true"

      FILE_SIZE_LIMIT_MB: 100

      # TODO: Change this to a unique secret for this server
      SESSION_SECRET: "f172e0f34532a0bee0b1709e32ce75fe9f2ed2e36eaf0cfc011cb5ba2cf15de3"

      STRATEGY_LOCAL: "true"
      DEBUG: "speckle:*"

      POSTGRES_URL: "postgres"
      POSTGRES_USER: "speckle"
      POSTGRES_PASSWORD: "speckle"
      POSTGRES_DB: "speckle"
      ENABLE_MP: "false"

      # TODO: Change this to the URL of the speckle server, as accessed from the network
      FRONTEND_ORIGIN: "${PUBLIC_URL}"

      EMAIL: "true"
      EMAIL_HOST: "abc-dk.mail.protection.outlook.com"
      EMAIL_PORT: "25"
      EMAIL_USERNAME: "."
      EMAIL_PASSWORD: "."
      EMAIL_FROM: "[email protected]"

  preview-service:
    image: speckle/speckle-preview-service:${SPECKLE_VERSION}
    restart: always
    depends_on:
      speckle-server:
        condition: service_healthy
    mem_limit: "1000m"
    memswap_limit: "1000m"
    environment:
      DEBUG: "preview-service:*"
      REDIS_URL: "redis://redis"
      PORT: "3001"

  webhook-service:
    image: speckle/speckle-webhook-service:${SPECKLE_VERSION}
    restart: always
    depends_on:
      speckle-server:
        condition: service_healthy
    environment:
      DEBUG: "webhook-service:*"
      PG_CONNECTION_STRING: "postgres://speckle:speckle@postgres/speckle"
      WAIT_HOSTS: postgres:5432

  fileimport-service:
    image: speckle/speckle-fileimport-service:${SPECKLE_VERSION}
    restart: always
    depends_on:
      speckle-server:
        condition: service_healthy
    environment:
      DEBUG: "fileimport-service:*"
      PG_CONNECTION_STRING: "postgres://speckle:speckle@postgres/speckle"
      WAIT_HOSTS: postgres:5432
      REDIS_URL: "redis://redis"

      S3_ENDPOINT: "http://minio:9000"
      S3_ACCESS_KEY: "minioadmin"
      S3_SECRET_KEY: "minioadmin"
      S3_BUCKET: "speckle-server"

      SPECKLE_SERVER_URL: "http://speckle-server:3000"

networks:
  default:
    name: speckle-server

volumes:
  postgres-data:
  redis-data:
  minio-data:

Found the problem…

As pointed out elsewhere the healthcheck needed updating. Same thread pointed to the S3_ENDPOINT being expected as publicly available having changed.

Any plans update the Docker Compose guide from the old dokumentation to match new releases or just make it available again?

Are the docs at Getting Started - Speckle Docs incorrect??

@MortenLund, is the docs page I referenced adequate for your request?

Hi @jonathon

Its probably okay, its no where as detailed as the old and is missing key parts for an actual deployment.

I also expected that a more complete guide would at some point be found in link at the top of the page you referenced (see link below).

The guide in the old documentation was chef’s kiss emoji and got os 98,2% of the way to our current deployment (before the problems we had).

docs reference to deployment guide for docker compose