# Quick-start: docker compose up # Uses prebuilt Spree image — no local build needed. # To customize the backend, run: docker compose -f docker-compose.dev.yml up x-app: &app image: ghcr.io/spree/spree:${SPREE_VERSION_TAG:-latest} depends_on: postgres: condition: service_healthy # Uncomment when enabling the optional meilisearch service below. # meilisearch: # condition: service_healthy mailpit: condition: service_started env_file: .env environment: DATABASE_URL: postgres://postgres@postgres:5432/spree_production SECRET_KEY_BASE: ${SECRET_KEY_BASE} RAILS_FORCE_SSL: ${RAILS_FORCE_SSL:-false} RAILS_ASSUME_SSL: ${RAILS_ASSUME_SSL:-false} # Public host for generated URLs (image/attachment URLs in API responses, # email links). Override in .env when serving from a real domain or IP. RAILS_HOST: ${RAILS_HOST:-localhost:${SPREE_PORT:-3000}} # Product search runs on the database by default. To switch to Meilisearch, # uncomment this together with the meilisearch depends_on/service/volume # blocks, then reindex: docker compose exec web bin/rails spree:search:reindex # MEILISEARCH_URL: http://meilisearch:7700 # All outgoing mail is captured by Mailpit — read it at http://localhost:8025. # Set SMTP_HOST (and friends) in .env to deliver through a real provider instead. SMTP_HOST: ${SMTP_HOST:-mailpit} SMTP_PORT: ${SMTP_PORT:-1025} services: postgres: image: postgres:18-alpine environment: POSTGRES_HOST_AUTH_METHOD: trust PGDATA: /var/lib/postgresql/18/docker volumes: - postgres_data:/var/lib/postgresql healthcheck: test: pg_isready -U postgres interval: 5s timeout: 5s retries: 5 # Optional Meilisearch search provider (typo tolerance, instant facets). # Uncomment together with the meilisearch lines under x-app's depends_on and # environment, and the meilisearch_data volume at the bottom. # meilisearch: # image: getmeili/meilisearch:latest # volumes: # - meilisearch_data:/meili_data # ports: # # Loopback-only: the app reaches Meilisearch over the compose network; # # the host port is for debugging and must not be LAN-reachable (Docker's # # NAT rules bypass host firewalls on Linux, and no API key is set). # - "127.0.0.1:${SPREE_MEILISEARCH_PORT:-7700}:7700" # healthcheck: # test: curl -f http://localhost:7700/health || exit 1 # interval: 5s # timeout: 5s # retries: 5 mailpit: image: axllent/mailpit:latest ports: # Loopback-only, matching meilisearch: reachable from the host browser # but not the LAN (Docker's NAT rules bypass host firewalls on Linux). # 8025 = web UI — open http://localhost:8025 to read captured mail. # 1025 = SMTP endpoint the app delivers to (in-cluster as mailpit:1025). - "127.0.0.1:${MAILPIT_UI_PORT:-8025}:8025" - "127.0.0.1:${MAILPIT_SMTP_PORT:-1025}:1025" environment: MP_MAX_MESSAGES: "500" MP_SMTP_AUTH_ACCEPT_ANY: "true" MP_SMTP_AUTH_ALLOW_INSECURE: "true" # One app container: Puma serves the web AND runs Solid Queue's job # supervisor in-process (SOLID_QUEUE_IN_PUMA, on by default). To split job # processing out, add a service from the same image with # `command: bin/jobs` and set SOLID_QUEUE_IN_PUMA: "false" here. web: <<: *app ports: - "${SPREE_PORT:-3000}:3000" volumes: - storage_data:/rails/storage - ./config/initializers/api_body_limit.rb:/rails/config/initializers/api_body_limit.rb:ro - ./config/initializers/image_studio.rb:/rails/config/initializers/image_studio.rb:ro - ./config/initializers/address_label.rb:/rails/config/initializers/address_label.rb:ro - ./config/initializers/customer_preferences.rb:/rails/config/initializers/customer_preferences.rb:ro - ./config/initializers/easyparcel_calculator.rb:/rails/config/initializers/easyparcel_calculator.rb:ro - ./config/routes.rb:/rails/config/routes.rb:ro - ./app/controllers/spree:/rails/app/controllers/spree:ro - ./app/views/spree:/rails/app/views/spree:ro healthcheck: test: curl -f http://localhost:3000/up || exit 1 interval: 10s timeout: 5s retries: 10 start_period: 30s volumes: postgres_data: # meilisearch_data: storage_data: