Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compose hosted returns 404 for all images #380

Closed
1 task done
troykelly opened this issue Oct 20, 2024 · 10 comments
Closed
1 task done

Compose hosted returns 404 for all images #380

troykelly opened this issue Oct 20, 2024 · 10 comments

Comments

@troykelly
Copy link

📜 Description

When trying to use postiz in a compose hosted environment - all images return 404.
I've tried setting the environment variable NEXT_PUBLIC_UPLOAD_DIRECTORY per https://docs.postiz.com/installation/docker-compose
I've tried setting the environment variable NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY per https://github.com/gitroomhq/postiz-app/blob/main/.env.example

Does not work

    - STORAGE_PROVIDER=local
    - UPLOAD_DIRECTORY=/uploads
    - NEXT_PUBLIC_UPLOAD_DIRECTORY=/uploads

Does not work

    - STORAGE_PROVIDER=local
    - UPLOAD_DIRECTORY=/uploads
    - NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY=/uploads

👟 Reproduction steps

  1. Generate an environment per the compose file instructions
  2. Try and use media uploaded to local storage
  3. Be sad at the 404

👍 Expected behavior

It should work

👎 Actual Behavior with Screenshots

Container Log

 ⨯ upstream image response failed for https://social.aperim.com/uploads/2024/10/20/82d104989abc73ed01fe9410e3decbd956.jpeg 404
 ⨯ upstream image response failed for https://social.aperim.com/uploads/2024/10/20/82d104989abc73ed01fe9410e3decbd956.jpeg 404

Files are there

2f97d44f15e2:/app# ls -al /uploads/2024/10/20/82d104989abc73ed01fe9410e3decbd956.jpeg
-rw-r--r--    1 root     root          1807 Oct 20 04:46 /uploads/2024/10/20/82d104989abc73ed01fe9410e3decbd956.jpeg
2f97d44f15e2:/app# ls -al /uploads/2024/10/20/82d104989abc73ed01fe9410e3decbd956.jpeg
-rw-r--r--    1 root     root          1807 Oct 20 04:46 /uploads/2024/10/20/82d104989abc73ed01fe9410e3decbd956.jpeg

💻 Operating system

Linux

🤖 Node Version

v20.17.0

📃 Provide any additional context for the Bug.

Compose

# Default options with logging configuration
x-default-opts: &default-opts
  logging:
    driver: gelf
    options:
      gelf-address: ${GELF_SERVER}
      gelf-compression-type: none
      tag: "{{.ImageName}}/{{.Name}}/{{.ID}}"
      labels: purpose,site

# Base service definition for Postiz
x-postiz-service: &postiz-service
  <<: *default-opts
  image: ghcr.io/gitroomhq/postiz-app:latest
  environment:
    - MAIN_URL=https://${SITE_DOMAIN}
    - FRONTEND_URL=https://${SITE_DOMAIN}
    - NEXT_PUBLIC_BACKEND_URL=https://api.${SITE_DOMAIN}
    - JWT_SECRET=${JWT_SECRET}
    - DATABASE_URL=postgresql://${POSTGRES_USER:-postiz-user}:${POSTGRES_PASSWORD}@postgres-${SITE}:5432/${POSTGRES_DB:-postiz-db-local}
    - REDIS_URL=redis://:${REDIS_PASSWORD}@redis-${SITE}:6379
    - BACKEND_INTERNAL_URL=http://localhost:3000
    - IS_GENERAL=${IS_GENERAL:-true}
    - STORAGE_PROVIDER=local
    - UPLOAD_DIRECTORY=/uploads
    - NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY=/uploads
    - OPENAI_API_KEY=${OPENAI_API_KEY:-}
    - EMAIL_PROVIDER
    - EMAIL_HOST
    - EMAIL_PORT
    - EMAIL_SECURE
    - EMAIL_USER
    - EMAIL_PASS
    - EMAIL_FROM_ADDRESS
    - EMAIL_FROM_NAME
    - X_API_KEY
    - X_API_SECRET
    - FACEBOOK_APP_ID
    - FACEBOOK_APP_SECRET
    - LINKEDIN_CLIENT_ID
    - LINKEDIN_CLIENT_SECRET
    - TIKTOK_CLIENT_ID
    - TIKTOK_CLIENT_SECRET

  volumes:
    - config:/config
    - uploads:/uploads
  networks:
    - backend
    - proxy
  sysctls:
    - net.ipv6.conf.all.disable_ipv6=0
  depends_on:
    - postgres
    - redis

services:

  postiz:
    <<: *postiz-service
    labels:
      - purpose=postiz
      - site=${SITE}
    deploy:
      replicas: ${POSTIZ_REPLICAS:-1}
      endpoint_mode: dnsrr
      placement:
        max_replicas_per_node: 1
        constraints:
          - node.role==worker
      rollback_config:
        parallelism: 2
        delay: 120s
        failure_action: continue
        monitor: 60s
        order: start-first
      update_config:
        parallelism: 2
        delay: 10s
        failure_action: rollback
        monitor: 60s
        order: start-first
      resources:
        limits:
          cpus: "6.00"
          memory: 6G
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
        window: 120s
      labels:
        - traefik.enable=true
        - traefik.docker.network=proxy
        - traefik.constraint-label=traefik-public

        #----------------------------------------------- routers for: postiz --------------------------------------------------
        # HTTP router
        - traefik.http.routers.postiz-${SITE}-http.rule=Host(`${SITE_DOMAIN}`)
        - traefik.http.routers.postiz-${SITE}-http.entrypoints=http
        - traefik.http.routers.postiz-${SITE}-http.middlewares=servicests,block-apple,https-redirect,reporturi
        - traefik.http.routers.postiz-${SITE}-http.service=noop@internal

        # HTTPS router
        - traefik.http.routers.postiz-${SITE}-https.rule=Host(`${SITE_DOMAIN}`)
        - traefik.http.routers.postiz-${SITE}-https.entrypoints=http3
        - traefik.http.routers.postiz-${SITE}-https.service=postiz-${SITE}
        - traefik.http.routers.postiz-${SITE}-https.tls.certresolver=${SITE_RESOLVER}
        - traefik.http.routers.postiz-${SITE}-https.middlewares=gzip,limit,reporturi,block-apple

        # HTTPS API router
        - traefik.http.routers.postiz-api-${SITE}-https.rule=Host(`api.${SITE_DOMAIN}`)
        - traefik.http.routers.postiz-api-${SITE}-https.entrypoints=http3
        - traefik.http.routers.postiz-api-${SITE}-https.service=postiz-api-${SITE}
        - traefik.http.routers.postiz-api-${SITE}-https.tls.certresolver=${SITE_RESOLVER}
        - traefik.http.routers.postiz-api-${SITE}-https.middlewares=cors-headers-${SITE},gzip,limit,reporturi,block-apple

        #----------------------------------------------- CORS Middleware --------------------------------------------------
        # Define CORS headers middleware
        - traefik.http.middlewares.cors-headers-${SITE}.headers.accessControlAllowOriginList=https://${SITE_DOMAIN}
        - traefik.http.middlewares.cors-headers-${SITE}.headers.accessControlAllowMethods=GET,OPTIONS,PUT,POST,DELETE
        - traefik.http.middlewares.cors-headers-${SITE}.headers.accessControlAllowHeaders=Origin,Content-Type,Accept,Authorization
        - traefik.http.middlewares.cors-headers-${SITE}.headers.accessControlAllowCredentials=true
        - traefik.http.middlewares.cors-headers-${SITE}.headers.addVaryHeader=true

        #====================================================== services ===========================================================
        - traefik.http.services.postiz-${SITE}.loadbalancer.server.port=4200
        - traefik.http.services.postiz-api-${SITE}.loadbalancer.server.port=3000
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "wget --no-verbose --tries=1 --spider http://localhost:4200/ && wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1",
        ]
      interval: 5s
      timeout: 2s
      retries: 20
      start_period: 60m

  postgres:
    <<: *default-opts
    image: postgis/postgis:${POSTGRES_MAJOR_VERSION}-${POSTGRES_MINOR_VERSION}.${POSTGRES_PATCH_VERSION}
    hostname: "postgres-${SITE}"
    networks:
      - backend
      - proxy
    labels:
      - purpose=postgres-odoo
      - site=${SITE}
    stop_grace_period: 600s
    deploy:
      replicas: 1
      endpoint_mode: dnsrr
      placement:
        max_replicas_per_node: 1
        constraints:
          - node.role==worker
      rollback_config:
        parallelism: 2
        delay: 120s
        failure_action: continue
        monitor: 60s
        order: stop-first
      update_config:
        parallelism: 2
        delay: 10s
        failure_action: rollback
        monitor: 60s
        order: stop-first
      resources:
        limits:
          cpus: "6.00"
          memory: 6G
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
        window: 120s
      labels:
        - traefik.enable=true
        - traefik.docker.network=proxy
        - traefik.constraint-label=traefik-public
        #----------------------------------------------- routers for: postgres --------------------------------------------------
        # postgres
        - traefik.tcp.routers.postgres-${SITE}.rule=HostSNI(`postgres.${SITE_DOMAIN}`)
        - traefik.tcp.routers.postgres-${SITE}.entrypoints=postgres
        - traefik.tcp.routers.postgres-${SITE}.service=postgres-${SITE}
        - traefik.tcp.routers.postgres-${SITE}.tls.certresolver=${SITE_RESOLVER}
        - traefik.tcp.routers.postgres-${SITE}.tls.domains[0].main=postgres.${SITE_DOMAIN}
        #====================================================== services ===========================================================
        - traefik.tcp.services.postgres-${SITE}.loadbalancer.server.port=${POSTGRES_PORT:-5432}
    environment:
      - POSTGRES_DB=${POSTGRES_DB:-postgres}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_USER=${POSTGRES_USER:-odoo}
      - POSTGRES_PORT=${POSTGRES_PORT:-5432}
      - PGDATA=/var/lib/postgresql/data/pgdata
      - PGADMIN=/var/lib/postgresql/data/pgadmin
      - PGPASSFILE=/root/.pgpass
      - POSTGRES_SYNCHRONOUS_COMMIT=on
    entrypoint:
      - "bash"
      - "-c"
      - |
        apt-get update &&
        DEBIAN_FRONTEND=noninteractive apt-get install -y postgresql-plpython3-$$PG_MAJOR python3-requests postgresql-$$PG_MAJOR-cron &&
        if [ -d "$$PGDATA" ] && [ "$$(ls -A $$PGDATA)" ]; then
            echo "#!/bin/bash
            set -e
            psql -v ON_ERROR_STOP=1 --username \"$$POSTGRES_USER\" --dbname \"$$POSTGRES_DB\" <<-EOSQL
              CREATE EXTENSION IF NOT EXISTS plpython3u;
            EOSQL" > /docker-entrypoint-initdb.d/50_plpython3.sh &&
            chmod +x /docker-entrypoint-initdb.d/50_plpython3.sh &&
            if ! grep -qF "shared_preload_libraries = 'pg_cron'" $$PGDATA/postgresql.conf; then
                echo "shared_preload_libraries = 'pg_cron'" >> $$PGDATA/postgresql.conf;
            fi &&
            if ! grep -qF "cron.database_name = '$${POSTGRES_DB}'" $$PGDATA/postgresql.conf; then
                echo "cron.database_name = '$${POSTGRES_DB}'" >> $$PGDATA/postgresql.conf;
            fi
        fi &&
        mkdir -p $$PGADMIN &&
        echo $$POSTGRES_PASSWORD > "$$PGADMIN/$${POSTGRES_DB}.$${POSTGRES_USER}" &&
        echo "{\"Servers\": {\"1\": {\"Name\": \"$${POSTGRES_DB} $$POSTGRES_USER\", \"Group\": \"Odoo\", \"Host\": \"$$(hostname)\", \"HostAddr\": \"$$(hostname)\", \"Port\": $${POSTGRES_PORT}, \"MaintenanceDB\": \"postgres\", \"Username\": \"$${POSTGRES_USER}\", \"SSLMode\": \"${ODOO_POSTGRES_SSL_MODE:-disable}\", \"Comment\": \"Odoo Postgres Server\", \"PassFile\": \"/data/pgadmin/$${POSTGRES_DB}.$${POSTGRES_USER}\", \"Shared\": true, \"SharedUsername\": \"$${POSTGRES_DB}-$$POSTGRES_USER\"}}}" > "$$PGADMIN/servers.json" &&
        chown -R 5050:5050 $${PGADMIN} &&
        chmod 700 $${PGADMIN} &&
        chmod 0600 "$$PGADMIN/$${POSTGRES_DB}.$${POSTGRES_USER}" &&
        echo "Creating pgAdmin data directory at $${PGADMIN}"
        echo 'localhost:5432:*:'$$POSTGRES_USER':'$$POSTGRES_PASSWORD > /root/.pgpass &&
        chown 999 /root/.pgpass &&
        chmod 0600 /root/.pgpass &&
        exec docker-entrypoint.sh postgres
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=0
    volumes:
      - "/etc/timezone:/etc/timezone:ro"
      - postgres:/var/lib/postgresql/data
      - type: tmpfs
        target: "/tmp"
        tmpfs:
          size: 2147483648
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "PGPASSWORD=$${POSTGRES_PASSWORD} pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB} && PGPASSWORD=$${POSTGRES_PASSWORD} psql -U $${POSTGRES_USER} -d template_postgis -tAc \"SELECT 1 FROM pg_extension WHERE extname='postgis';\" | grep -q 1 || exit 1",
        ]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 60m

  postgres-backup:
    <<: *default-opts
    image: postgis/postgis:${POSTGRES_MAJOR_VERSION}-${POSTGRES_MINOR_VERSION}.${POSTGRES_PATCH_VERSION}
    networks:
      - backend
    labels:
      - purpose=postgres-backup
      - site=${SITE}
    deploy:
      replicas: 1
      endpoint_mode: dnsrr
      placement:
        max_replicas_per_node: 1
        constraints:
          - node.role==worker
      rollback_config:
        parallelism: 2
        delay: 120s
        failure_action: continue
        monitor: 60s
        order: start-first
      update_config:
        parallelism: 2
        delay: 10s
        failure_action: rollback
        monitor: 60s
        order: start-first
      resources:
        limits:
          cpus: "6.00"
          memory: 1G
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
        window: 120s
    environment:
      - POSTGRES_DB=${POSTGRES_DB:-postgres}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_USER=${POSTGRES_USER:-odoo}
      - BACKUP_DESTINATION=/backups/dump
      - REMOVE_BEFORE=60
    command:
      - "bash"
      - "-c"
      - |
        apt-get update &&
        DEBIAN_FRONTEND=noninteractive apt-get install -y postgresql-client &&
        BACKUP_INTERVAL=$${BACKUP_INTERVAL:-12} &&
        WAIT_SECONDS=60 &&
        MAX_TRIES=60 &&
        try=0 &&
        until pg_isready -h postgres -U $${POSTGRES_USER}; do
         if [ $$try -gt $$MAX_TRIES ]; then
           echo "Postgres service did not start within the expected time." && exit 1;
         fi
         sleep $$WAIT_SECONDS && let try+=1;
        done &&
        while true; do
          YEAR=`date +"%Y"` && MONTH=`date +"%B"` && DAY=`date +"%d"` && TIME=`date +"%H%M"` &&
          DESTINATION_PATH=$${BACKUP_DESTINATION:-/backups}/$$YEAR/$$MONTH/$$DAY$$TIME &&
          mkdir -p $$DESTINATION_PATH &&
          BACKUP_FILE=$$DESTINATION_PATH/dump.sql &&
          COMPRESSED_FILE=$$BACKUP_FILE.gz &&
          if PGPASSWORD=$${POSTGRES_PASSWORD} pg_dumpall -U $${POSTGRES_USER} -h postgres --clean --if-exists --inserts --on-conflict-do-nothing --quote-all-identifiers --no-password -f $$BACKUP_FILE; then
            gzip $$BACKUP_FILE &&
            echo "Backup taken at $$YEAR/$$MONTH/$$DAY$$TIME" &&
            echo "To restore this backup, use the following command:" &&
            echo "export PGPASSWORD=[your_password] && gunzip -c $$COMPRESSED_FILE | psql -h localhost -U $${POSTGRES_USER} -d $${POSTGRES_DB}"
          else
            echo "Backup failed at $$YEAR/$$MONTH/$$DAY$$TIME"
          fi &&
          find $${BACKUP_DESTINATION:-/backups}/ -mtime +$${REMOVE_BEFORE:-60} -type f -delete &&
          sleep $${BACKUP_INTERVAL}h;
        done
    volumes:
      - "/etc/timezone:/etc/timezone:ro"
      - postgres:/backups

  redis:
    <<: *default-opts
    image: "redis:latest"
    hostname: "redis-${SITE}"
    networks:
      - backend
      - proxy
    labels:
      - purpose=odoo-redis
      - site=${SITE}
    command: redis-server --appendonly yes --replica-read-only no --requirepass ${REDIS_PASSWORD}
    volumes:
      - "/etc/timezone:/etc/timezone:ro"
      - "redis-data:/data"
      - type: tmpfs
        target: "/tmp"
        tmpfs:
          size: 536870912
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=0
    deploy:
      replicas: 1
      endpoint_mode: dnsrr
      placement:
        max_replicas_per_node: 1
        constraints:
          - node.role==worker
      rollback_config:
        parallelism: 2
        delay: 120s
        failure_action: continue
        monitor: 60s
        order: stop-first
      update_config:
        parallelism: 2
        delay: 10s
        failure_action: rollback
        monitor: 60s
        order: stop-first
      resources:
        limits:
          cpus: "6.00"
          memory: 1G
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
        window: 120s
      labels:
        - traefik.enable=true
        - traefik.docker.network=proxy
        - traefik.constraint-label=traefik-public
        #----------------------------------------------- routers for: redis --------------------------------------------------
        # redis
        - traefik.tcp.routers.redis-${SITE}.rule=HostSNI(`redis.${SITE_DOMAIN}`)
        - traefik.tcp.routers.redis-${SITE}.entrypoints=redis
        - traefik.tcp.routers.redis-${SITE}.service=redis-${SITE}
        - traefik.tcp.routers.redis-${SITE}.tls.certresolver=${SITE_RESOLVER}
        - traefik.tcp.routers.redis-${SITE}.tls.domains[0].main=redis.${SITE_DOMAIN}
        #====================================================== services ===========================================================
        - traefik.tcp.services.redis-${SITE}.loadbalancer.server.port=6379
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      retries: 3
      timeout: 5s
      start_period: 10m

volumes:
  postgres:
    driver: glusterfs
    name: ${GLUSTER_VOLUME_POSTGRES}
  redis-data:
    driver: glusterfs
    name: ${GLUSTER_VOLUME_REDIS}
  config:
    driver: glusterfs
    name: ${GLUSTER_VOLUME_CONFIG}
  uploads:
    driver: glusterfs
    name: ${GLUSTER_VOLUME_UPLOADS}

networks:
  proxy:
    name: proxy
    driver: overlay
    external: true
  backend:
    driver: overlay

👀 Have you spent some time to check if this bug has been raised before?

  • I checked and didn't find similar issue

Are you willing to submit PR?

Yes I am willing to submit a PR!

@troykelly
Copy link
Author

The Traefik Instructions don't mention that bypassing the caddy service in the container prevents access to /uploads

A fix hack for this is:

      labels:
        - traefik.enable=true
        - traefik.docker.network=proxy
        - traefik.constraint-label=traefik-public

        #----------------------------------------------- routers for: postiz --------------------------------------------------
        # HTTP router
        - traefik.http.routers.postiz-${SITE}-http.rule=Host(`${SITE_DOMAIN}`)
        - traefik.http.routers.postiz-${SITE}-http.entrypoints=http
        - traefik.http.routers.postiz-${SITE}-http.middlewares=servicests,block-apple,https-redirect,reporturi
        - traefik.http.routers.postiz-${SITE}-http.service=noop@internal

        # HTTPS router for uploads
        - traefik.http.routers.postiz-uploads-${SITE}-https.rule=Host(`${SITE_DOMAIN}`) && PathPrefix(`/uploads/`)
        - traefik.http.routers.postiz-uploads-${SITE}-https.entrypoints=http3
        - traefik.http.routers.postiz-uploads-${SITE}-https.service=postiz-uploads-${SITE}
        - traefik.http.routers.postiz-uploads-${SITE}-https.tls.certresolver=${SITE_RESOLVER}
        - traefik.http.routers.postiz-uploads-${SITE}-https.middlewares=gzip,limit,reporturi,block-apple
        - traefik.http.routers.postiz-uploads-${SITE}-https.priority=20

        # HTTPS router for frontend
        - traefik.http.routers.postiz-${SITE}-https.rule=Host(`${SITE_DOMAIN}`) && !PathPrefix(`/uploads/`)
        - traefik.http.routers.postiz-${SITE}-https.entrypoints=http3
        - traefik.http.routers.postiz-${SITE}-https.service=postiz-${SITE}
        - traefik.http.routers.postiz-${SITE}-https.tls.certresolver=${SITE_RESOLVER}
        - traefik.http.routers.postiz-${SITE}-https.middlewares=gzip,limit,reporturi,block-apple
        - traefik.http.routers.postiz-${SITE}-https.priority=10

        # HTTPS API router
        - traefik.http.routers.postiz-api-${SITE}-https.rule=Host(`api.${SITE_DOMAIN}`)
        - traefik.http.routers.postiz-api-${SITE}-https.entrypoints=http3
        - traefik.http.routers.postiz-api-${SITE}-https.service=postiz-api-${SITE}
        - traefik.http.routers.postiz-api-${SITE}-https.tls.certresolver=${SITE_RESOLVER}
        - traefik.http.routers.postiz-api-${SITE}-https.middlewares=cors-headers-${SITE},gzip,limit,reporturi,block-apple

        #----------------------------------------------- CORS Middleware --------------------------------------------------
        # Define CORS headers middleware
        - traefik.http.middlewares.cors-headers-${SITE}.headers.accessControlAllowOriginList=https://${SITE_DOMAIN}
        - traefik.http.middlewares.cors-headers-${SITE}.headers.accessControlAllowMethods=GET,OPTIONS,PUT,POST,DELETE
        - traefik.http.middlewares.cors-headers-${SITE}.headers.accessControlAllowHeaders=Origin,Content-Type,Accept,Authorization
        - traefik.http.middlewares.cors-headers-${SITE}.headers.accessControlAllowCredentials=true
        - traefik.http.middlewares.cors-headers-${SITE}.headers.addVaryHeader=true

        #====================================================== services ===========================================================
        - traefik.http.services.postiz-${SITE}.loadbalancer.server.port=4200
        - traefik.http.services.postiz-api-${SITE}.loadbalancer.server.port=3000
        - traefik.http.services.postiz-uploads-${SITE}.loadbalancer.server.port=5000

@jamesread
Copy link
Collaborator

Heya @troykelly , you're using the wrong syntax to specify environment variables in your docker-compose, you're using an = sign in your pasted examples, but the syntax is to use a : indocker-compose.

Not this

    - STORAGE_PROVIDER=local

Do this instead

    - STORAGE_PROVIDER: local

You will need to docker compose down and docker-compose up again to recreate containers in the correct format.

@troykelly
Copy link
Author

@jamesread No? I'm using mapping syntax. https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-environment-attribute

If you want to use a colon you don't use the hyphen:

services:
  webapp:
    environment:
      DEBUG: "true"

@troykelly
Copy link
Author

Just to clarify - the issue doesn't appear to be the settings. It's that the traefik instructions don't mention the need to also send traffic to the port 5000 caddy server for uploads/ - I've added that to the config and it's working.

Because there is a conflict in the documentation - I'm just setting both NEXT_PUBLIC_UPLOAD_DIRECTORY and NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY

@MikeKMiller
Copy link

I can confirm, this applies to all docker compose deployments, and the documentation is not clear that you MUST only point to port 5000 for the uploads to work.

@troykelly
Copy link
Author

I can confirm, this applies to all docker compose deployments, and the documentation is not clear that you MUST only point to port 5000 for the uploads to work.

I ended up doing the split pointing because when I only used 5000 nothing would work.

It might be better to have an "entry level" one container to rule them all (as is mostly the case now) and a set of containers for frontend, backend, static/uploads file service. -given it's generally best practice for a container to do one thing - not all the things.

@jamesread
Copy link
Collaborator

Hey @troykelly,

Sorry that it's taken me a little while to get back to you.

@jamesread No? I'm using mapping syntax. https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-environment-attribute

I'm sorry, I was wrong, you are absolutely right. I'm just so used to seeing the YAML map syntax that = syntax looks wrong to me. You're right, it's not the cause of the problem here.

The Traefik Instructions don't mention that bypassing the caddy service in the container prevents access to /uploads

I've just adjusted the diagram to include this;

image

Equally, the Traefik docs were written before the introduction of the Caddy internal proxy (because we wanted to do stuff like host /uploads), and get around loads of CORs issues.

@troykelly @MikeKMiller Would you mind looking at teh adjusted docs for Traefik, and check that it reads a little more sane now?

I ended up doing the split pointing because when I only used 5000 nothing would work.

Of course you should be able to still do this split configuration (4200, 3000, etc), but generally we have been trying to make it easier on everyone by just having one port - 5000, and we've seen the support tickets on this area massively decrease.

If you have a working setup, great, but also consider if you'd like you should be able to simplify by just using port 5000.

It might be better to have an "entry level" one container to rule them all (as is mostly the case now) and a set of containers for frontend, backend, static/uploads file service. -given it's generally best practice for a container to do one thing - not all the things.

It is possible to just start up single services within the same container image - http://localhost:3000/installation/docker-compose#controlling-container-services - however this approach just means Postiz uses more RAM / CPU / Storage without being any more stable. We don't want to build more container images, as it doesn't really solve any problems anyone has right now.

Keen to know if the docs are now better for you, keen to take your suggestions and comments until they're improved!

@troykelly
Copy link
Author

@jamesread traefik documentation looks fine now, thank you.

I did originally try just to port 5000 but was getting the 404 error on everything. I think that might have been because of the NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY / NEXT_PUBLIC_UPLOAD_DIRECTORY issue - and it possibly was working, but just not getting the config it needed.

My apologies for missing https://docs.postiz.com/installation/docker-compose#controlling-container-services. Thank you. I will update our configuration accordingly to separate things.

I typically try to keep to one container, one service:

  1. Isolation: Separating services into individual containers ensures that they don't interfere with each other's dependencies or resource usage. This isolation can make debugging and scaling specific components easier. Dockerfile Best Practices - Decouple Applications
  2. Scalability: When services are containerized separately, you can scale them independently based on demand. For example, if the backend API requires more resources than the frontend, you can allocate accordingly without affecting the other services. Scaling Services in Docker Swarm Kubernetes Horizontal Pod Autoscaler
  3. Maintainability: Single-purpose containers are generally more straightforward to maintain and update. If a service needs to be updated or redeployed, it can be done without impacting the rest of the application.
  4. Fault Isolation: If one service fails or needs to be restarted, it won't bring down the entire application. This improves the overall resilience of the system.
  5. Security: Isolating services can reduce the attack surface and limit the impact of a security breach to a single component. Docker Security Overview

I completely understand the desire to optimize resource usage, especially when deploying to environments with limited resources.

I'd be happy to discuss this further or help with any adjustments if you're interested. Perhaps two examples with shared services and single-purpose containers? Again, thank you for your amazing work on Postiz!

@jamesread
Copy link
Collaborator

Hey @troykelly

@jamesread traefik documentation looks fine now, thank you.

Great, and thanks for your patience :-) Sorry for misreading your last post so badly!

I typically try to keep to one container, one service:

I don't necessarily disagree with anything you've written there - I personally run Kubernetes for most of my stuff at home (although my personal Postiz instance actually does run on a docker VM), but just saying that I understand service isolation and those concepts, etc.

We're seeing that the vast majority of users picking up Postiz are running on their laptops, or are non-technical folks, so if we did do something in addition to the "monolithic" container image we'd have to display it or make it available separately so we're not confusing anybody. At this moment this project literally has SO many new users coming in that it's difficult to keep up with support tickets as it is! Adding more installation options comes at the cost of a higher support overhead too - so we'd need to be clear that this is something a bunch of people want.

Inside the existing "monolithic" container, there is a supervisord, so you do have at least a level of control over starting/stopping services. It's not the same as individual container controls, but it's something. I'm hoping that setting POSTIZ_APPs="worker", etc works for you for the time being - but come back if this approach has downsides?

Do you think it's OK to close this issue?

@troykelly
Copy link
Author

All great @jamesread - thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants