AWS S3 for Docker Compose deployment

Hi guys,

I am deploying using the Docker Compose and trying to manage the dependencies and move them into their individual services. I seem to have figured out the Postgres and Redis configurations (unverified, but they have passed the erroring out portion during startup) and I have hit a problem with the S3 configurations.

This is the error i’m getting:

    err: {
      "type": "EnvironmentResourceError",
      "message": "Can't open S3 bucket '{bucket}', and the Speckle server configuration has disabled creation of the bucket.: Invalid URL",
      "stack":
          EnvironmentResourceError: Can't open S3 bucket '{bucket}', and the Speckle server configuration has disabled creation of the bucket.: Invalid URL
              at /speckle-server/packages/server/dist/modules/blobstorage/repositories/blobs.js:88:19
              at async ensureConditions (/speckle-server/packages/server/dist/modules/blobstorage/index.js:17:5)
              at async Object.init (/speckle-server/packages/server/dist/modules/blobstorage/index.js:22:5)
              at async Object.init (/speckle-server/packages/server/dist/modules/index.js:116:9)
              at async init (/speckle-server/packages/server/dist/app.js:280:5)
          caused by: TypeError [ERR_INVALID_URL]: Invalid URL
              at new NodeError (node:internal/errors:405:5)
              at new URL (node:internal/url:676:13)
              at parseUrl (/speckle-server/node_modules/@aws-sdk/url-parser/dist-cjs/index.js:7:38)
              at toEndpointV1 (/speckle-server/node_modules/@aws-sdk/middleware-endpoint/dist-cjs/adaptors/toEndpointV1.js:12:38)
              at customEndpointProvider (/speckle-server/node_modules/@aws-sdk/middleware-endpoint/dist-cjs/resolveEndpointConfig.js:10:99)
      "name": "EnvironmentResourceError",
      "jse_shortmsg": "Can't open S3 bucket '{bucket}', and the Speckle server configuration has disabled creation of the bucket.",
      "jse_cause": {
        "type": "TypeError",
        "message": "Invalid URL",
        "stack":
            TypeError [ERR_INVALID_URL]: Invalid URL
                at new NodeError (node:internal/errors:405:5)
                at new URL (node:internal/url:676:13)
                at parseUrl (/speckle-server/node_modules/@aws-sdk/url-parser/dist-cjs/index.js:7:38)
                at toEndpointV1 (/speckle-server/node_modules/@aws-sdk/middleware-endpoint/dist-cjs/adaptors/toEndpointV1.js:12:38)
                at customEndpointProvider (/speckle-server/node_modules/@aws-sdk/middleware-endpoint/dist-cjs/resolveEndpointConfig.js:10:99)
        "input": "{S3_ENDPOINT}",
        "code": "ERR_INVALID_URL"
      },
      "jse_info": {
        "bucket": "//REDACTED",
        "code": "ENVIRONMENT_RESOURCE_ERROR",
        "statusCode": 502
      }
    }

(side note probably missing a $ to print the actual bucket name in the log)

I have verified the S3_ACCESS_KEY and S3_SECRET_KEY works using AWS-cli. Also filling S3_PUBLIC_ENDPOINT doesn’t seem to make a difference. I’ve even made the bucket public read (for now) as per outlined in this post. (Please tell me if this is outdated or not too so I may know how to make the bucket auth only for example)

I’ve tried various formats (http(s)://)s3(.region).amazonaws.com

I’ll post my yml for completeness just in case it matters:

name: "speckle-server"

services:
  ####
  # Speckle Server dependencies (Reverse proxy only)
  #######
  reverse-proxy:
    image: traefik:v2.5
    restart: always
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.websecure.address=:443"
      - "--entryPoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.websecure.http.tls.domains[0].main=//REDACTED"
      - "--entrypoints.websecure.http.tls.domains[0].sans=//REDACTED"
      - "--certificatesresolvers.myresolver.acme.email=//REDACTED"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme-v1.json"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  ####
  # Speckle Server (now using external services)
  #######
  speckle-ingress:
    image: speckle/speckle-docker-compose-ingress:2
    restart: always
    ports: []
    environment:
      FILE_SIZE_LIMIT_MB: "100"
      NGINX_ENVSUBST_OUTPUT_DIR: "/etc/nginx"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.speckle-ingress.rule=Host(`//REDACTED`)"
      - "traefik.http.routers.speckle-ingress.entrypoints=websecure"
      - "traefik.http.routers.speckle-ingress.tls.certresolver=myresolver"
      - "traefik.http.services.speckle-ingress.loadbalancer.server.port=8080"
    depends_on:
      - speckle-server
      - speckle-frontend-2

  speckle-frontend-2:
    image: speckle/speckle-frontend-2:2
    restart: always
    environment:
      NUXT_PUBLIC_SERVER_NAME: "local"
      NUXT_PUBLIC_API_ORIGIN: "${SPECKLE_SERVER_URL}"
      NUXT_PUBLIC_BACKEND_API_ORIGIN: "http://speckle-server:3000"
      # External Redis connection (Upstash)
      NUXT_REDIS_URL: "${EXTERNAL_REDIS_URL}"
      NUXT_PUBLIC_BASE_URL: "${SPECKLE_SERVER_URL}"
      NUXT_PUBLIC_LOG_LEVEL: 'warn'
      LOG_LEVEL: 'info'
      LOG_PRETTY: 'true'
    depends_on:
      - speckle-server

  speckle-server:
    image: speckle/speckle-server:2
    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:'/graphql?query={serverInfo{version}}', 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: 3s
      retries: 30
    ports:
      - "3000:3000"
    environment:
      CANONICAL_URL: "${SPECKLE_SERVER_URL}"
      SPECKLE_AUTOMATE_URL: "http://127.0.0.1:3030"

      # External Redis
      REDIS_URL: "${EXTERNAL_REDIS_URL}"

      # External S3-compatible storage (AWS S3)
      S3_ENDPOINT: "{S3_ENDPOINT}"
      S3_PUBLIC_ENDPOINT : "{S3_ENDPOINT}"
      S3_BUCKET: "${S3_BUCKET}"
      S3_REGION: "{S3_REGION}"
      S3_ACCESS_KEY: "${S3_AKEY}"
      S3_SECRET_KEY: "${S3_SKEY}"
      S3_CREATE_BUCKET: "false"  # Bucket already created

      FILE_SIZE_LIMIT_MB: 100
      SESSION_SECRET: "${SPECKLE_SESSION_SECRET}"
      STRATEGY_LOCAL: "true"
      DEBUG: "speckle:*"

      # External PostgreSQL
      POSTGRES_URL: "${PG_HOST}"
      POSTGRES_USER: "${PG_USER}"
      POSTGRES_PASSWORD: "${PG_PASS}"
      POSTGRES_DB: "${PG_DB}"
      POSTGRES_PORT: "${PG_PORT:-5432}"
      
      ENABLE_MP: "false"
      FRONTEND_ORIGIN: "${SPECKLE_SERVER_URL}"

      LOG_LEVEL: 'info'
      LOG_PRETTY: 'true'

  preview-service:
    image: speckle/speckle-preview-service:2
    restart: always
    # Remove MinIO dependency for S3 testing
    depends_on:
      speckle-server:
        condition: service_healthy
    mem_limit: "1000m"
    memswap_limit: "1000m"
    environment:
      DEBUG: "preview-service:*"
      REDIS_URL: "${EXTERNAL_REDIS_URL}"
      PORT: "3001"

  webhook-service:
    image: speckle/speckle-webhook-service:2
    restart: always
    depends_on:
      speckle-server:
        condition: service_healthy
    environment:
      DEBUG: "webhook-service:*"
      PG_CONNECTION_STRING: "postgresql://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT:-5432}/${PG_DB}"

  fileimport-service:
    image: speckle/speckle-fileimport-service:2
    restart: always
    depends_on:
      speckle-server:
        condition: service_healthy
    environment:
      DEBUG: "fileimport-service:*"
      PG_CONNECTION_STRING: "postgresql://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT:-5432}/${PG_DB}"

      # External S3-compatible storage (AWS S3)
      S3_ENDPOINT: "{S3_ENDPOINT}"
      S3_PUBLIC_ENDPOINT : "{S3_ENDPOINT}"
      S3_BUCKET: "${S3_BUCKET}"
      S3_REGION: "{S3_REGION}"
      S3_ACCESS_KEY: "${S3_AKEY}"
      S3_SECRET_KEY: "${S3_SKEY}"
      S3_CREATE_BUCKET: "false"  # Bucket already created

      
      REDIS_URL: "${EXTERNAL_REDIS_URL}"
      SPECKLE_SERVER_URL: "http://speckle-server:3000"

networks:
  default:
    name: speckle-server

I forgot to add, the Docker Compose getting started doc 's link to advanced configurations, point to itself, so its not much of a help.