Medusa Headless Commerce
Open-source Shopify alternative with headless architecture.
Overview
Medusa is a modern Node.js-based headless commerce platform that provides a flexible alternative to Shopify, focusing on API-first architecture and developer experience. Built with TypeScript and designed for customization, Medusa offers comprehensive e-commerce functionality including product management, order processing, customer management, and payment handling through a robust REST API. The platform emphasizes modularity and extensibility, allowing developers to build custom storefronts using any frontend technology while maintaining powerful backend commerce capabilities. This stack combines Medusa's commerce engine with PostgreSQL for transactional data integrity, Redis for session management and caching, and MinIO for self-hosted object storage, creating a complete commerce infrastructure. PostgreSQL ensures ACID compliance for critical order and payment data, while Redis provides fast session storage and job queue processing for background tasks like email notifications and inventory updates. MinIO handles product images, digital downloads, and media assets with S3-compatible APIs, eliminating dependency on external cloud storage providers. E-commerce startups, agencies building custom storefronts, and companies migrating from monolithic platforms like Shopify Plus will benefit from this stack's flexibility and cost-effectiveness. The headless architecture enables omnichannel commerce experiences across web, mobile, and IoT devices while maintaining centralized inventory and order management through Medusa's comprehensive admin interface.
Key Features
- Complete headless commerce API with product catalog, inventory, orders, and customer management
- Multi-region and multi-currency support with localized pricing and tax calculation
- Built-in payment provider integrations including Stripe, PayPal, and manual payment methods
- Advanced product variant management with unlimited options and custom attributes
- Discount engine supporting percentage, fixed amount, and buy-X-get-Y promotional rules
- PostgreSQL JSONB fields for flexible product metadata and custom attributes storage
- Redis-powered job queue system for asynchronous email sending and webhook processing
- MinIO integration for product images, digital downloads, and customer file uploads with S3-compatible URLs
Common Use Cases
- 1Building custom D2C e-commerce stores with React, Vue, or Next.js frontends
- 2Creating multi-tenant marketplace platforms with separate vendor catalogs and commission tracking
- 3Developing mobile commerce apps with native iOS/Android frontends consuming Medusa APIs
- 4Implementing B2B wholesale portals with custom pricing tiers and approval workflows
- 5Setting up subscription commerce platforms with recurring billing and customer portals
- 6Deploying white-label e-commerce solutions for agencies serving multiple clients
- 7Creating omnichannel retail experiences connecting online stores with POS systems
Prerequisites
- Minimum 4GB RAM (2GB for PostgreSQL, 1GB for Medusa server, 512MB each for Redis and MinIO)
- Node.js 18+ knowledge for Medusa customization and plugin development
- PostgreSQL administration skills for backup, maintenance, and performance tuning
- Understanding of JWT authentication and session management concepts
- Ports 7001, 9000, and 9001 available for admin interface, API server, and MinIO console
- Basic S3 API knowledge for implementing file upload workflows in custom frontends
For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms
docker-compose.yml
docker-compose.yml
1services: 2 medusa-server: 3 build: 4 context: .5 dockerfile: Dockerfile6 ports: 7 - "9000:9000"8 environment: 9 NODE_ENV: development10 DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}11 REDIS_URL: redis://redis:637912 JWT_SECRET: ${JWT_SECRET}13 COOKIE_SECRET: ${COOKIE_SECRET}14 MINIO_ENDPOINT: minio15 MINIO_BUCKET: medusa16 MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}17 MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}18 depends_on: 19 postgres: 20 condition: service_healthy21 redis: 22 condition: service_started23 networks: 24 - medusa-net25 restart: unless-stopped2627 medusa-admin: 28 image: medusajs/admin:latest29 ports: 30 - "7001:80"31 environment: 32 MEDUSA_BACKEND_URL: http://medusa-server:900033 depends_on: 34 - medusa-server35 networks: 36 - medusa-net37 restart: unless-stopped3839 postgres: 40 image: postgres:16-alpine41 environment: 42 POSTGRES_USER: ${POSTGRES_USER}43 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}44 POSTGRES_DB: ${POSTGRES_DB}45 volumes: 46 - postgres_data:/var/lib/postgresql/data47 healthcheck: 48 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]49 interval: 10s50 timeout: 5s51 retries: 552 networks: 53 - medusa-net54 restart: unless-stopped5556 redis: 57 image: redis:7-alpine58 volumes: 59 - redis_data:/data60 networks: 61 - medusa-net62 restart: unless-stopped6364 minio: 65 image: minio/minio:latest66 ports: 67 - "9001:9001"68 environment: 69 MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}70 MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}71 volumes: 72 - minio_data:/data73 command: server /data --console-address ":9001"74 networks: 75 - medusa-net76 restart: unless-stopped7778volumes: 79 postgres_data: 80 redis_data: 81 minio_data: 8283networks: 84 medusa-net: 85 driver: bridge.env Template
.env
1# Medusa Configuration2JWT_SECRET=$(openssl rand -hex 32)3COOKIE_SECRET=$(openssl rand -hex 32)45# PostgreSQL6POSTGRES_USER=medusa7POSTGRES_PASSWORD=secure_postgres_password8POSTGRES_DB=medusa910# MinIO11MINIO_ACCESS_KEY=minioadmin12MINIO_SECRET_KEY=secure_minio_passwordUsage Notes
- 1Create Medusa project: npx create-medusa-app
- 2Medusa Admin at http://localhost:7001
- 3API at http://localhost:9000
- 4Run migrations: medusa migrations run
Individual Services(5 services)
Copy individual services to mix and match with your existing compose files.
medusa-server
medusa-server:
build:
context: .
dockerfile: Dockerfile
ports:
- "9000:9000"
environment:
NODE_ENV: development
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET}
COOKIE_SECRET: ${COOKIE_SECRET}
MINIO_ENDPOINT: minio
MINIO_BUCKET: medusa
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
networks:
- medusa-net
restart: unless-stopped
medusa-admin
medusa-admin:
image: medusajs/admin:latest
ports:
- "7001:80"
environment:
MEDUSA_BACKEND_URL: http://medusa-server:9000
depends_on:
- medusa-server
networks:
- medusa-net
restart: unless-stopped
postgres
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test:
- CMD-SHELL
- pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}
interval: 10s
timeout: 5s
retries: 5
networks:
- medusa-net
restart: unless-stopped
redis
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
networks:
- medusa-net
restart: unless-stopped
minio
minio:
image: minio/minio:latest
ports:
- "9001:9001"
environment:
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
volumes:
- minio_data:/data
command: server /data --console-address ":9001"
networks:
- medusa-net
restart: unless-stopped
Quick Start
terminal
1# 1. Create the compose file2cat > docker-compose.yml << 'EOF'3services:4 medusa-server:5 build:6 context: .7 dockerfile: Dockerfile8 ports:9 - "9000:9000"10 environment:11 NODE_ENV: development12 DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}13 REDIS_URL: redis://redis:637914 JWT_SECRET: ${JWT_SECRET}15 COOKIE_SECRET: ${COOKIE_SECRET}16 MINIO_ENDPOINT: minio17 MINIO_BUCKET: medusa18 MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}19 MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}20 depends_on:21 postgres:22 condition: service_healthy23 redis:24 condition: service_started25 networks:26 - medusa-net27 restart: unless-stopped2829 medusa-admin:30 image: medusajs/admin:latest31 ports:32 - "7001:80"33 environment:34 MEDUSA_BACKEND_URL: http://medusa-server:900035 depends_on:36 - medusa-server37 networks:38 - medusa-net39 restart: unless-stopped4041 postgres:42 image: postgres:16-alpine43 environment:44 POSTGRES_USER: ${POSTGRES_USER}45 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}46 POSTGRES_DB: ${POSTGRES_DB}47 volumes:48 - postgres_data:/var/lib/postgresql/data49 healthcheck:50 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]51 interval: 10s52 timeout: 5s53 retries: 554 networks:55 - medusa-net56 restart: unless-stopped5758 redis:59 image: redis:7-alpine60 volumes:61 - redis_data:/data62 networks:63 - medusa-net64 restart: unless-stopped6566 minio:67 image: minio/minio:latest68 ports:69 - "9001:9001"70 environment:71 MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}72 MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}73 volumes:74 - minio_data:/data75 command: server /data --console-address ":9001"76 networks:77 - medusa-net78 restart: unless-stopped7980volumes:81 postgres_data:82 redis_data:83 minio_data:8485networks:86 medusa-net:87 driver: bridge88EOF8990# 2. Create the .env file91cat > .env << 'EOF'92# Medusa Configuration93JWT_SECRET=$(openssl rand -hex 32)94COOKIE_SECRET=$(openssl rand -hex 32)9596# PostgreSQL97POSTGRES_USER=medusa98POSTGRES_PASSWORD=secure_postgres_password99POSTGRES_DB=medusa100101# MinIO102MINIO_ACCESS_KEY=minioadmin103MINIO_SECRET_KEY=secure_minio_password104EOF105106# 3. Start the services107docker compose up -d108109# 4. View logs110docker compose logs -fOne-Liner
Run this command to download and set up the recipe in one step:
terminal
1curl -fsSL https://docker.recipes/api/recipes/medusa-ecommerce/run | bashTroubleshooting
- Medusa migrations fail with 'relation does not exist': Run 'medusa migrations run' after PostgreSQL is fully initialized and healthy
- Admin UI shows 'Cannot connect to server': Verify MEDUSA_BACKEND_URL environment variable points to http://medusa-server:9000, not localhost
- Product image uploads return 403 errors: Check MinIO bucket policy allows public read access and MINIO_ENDPOINT uses service name 'minio'
- Redis connection timeouts during high traffic: Increase Redis maxmemory setting and configure appropriate eviction policy for session data
- PostgreSQL connection pool exhausted errors: Adjust Medusa database pool size in medusa-config.js based on expected concurrent users
- JWT token validation fails after container restart: Ensure JWT_SECRET and COOKIE_SECRET environment variables are persistent across deployments
Community Notes
Loading...
Loading notes...
Download Recipe Kit
Get all files in a ready-to-deploy package
Includes docker-compose.yml, .env template, README, and license
Components
medusa-serverpostgresqlredisminioadmin-ui
Tags
#medusa#ecommerce#headless#shopify-alternative#nodejs
Category
E-Commerce & BusinessAd Space
Shortcuts: C CopyF FavoriteD Download