Installing Medusa is genuinely one command. The interesting part is the twenty minutes afterwards: what got created, which defaults are wrong for anything real, and what to change before you write a line of your own code.
Prerequisites
| Requirement | Version | Notes |
|---|---|---|
| Node.js | 20 or 22 | Odd-numbered releases are not supported |
| PostgreSQL | 15+ | Local, Docker or hosted |
| Git | Any | |
| Redis | 7+ | Optional locally, required in production |
Confirm Postgres is actually reachable before you start. Roughly half of all failed installs are a database that is installed but not running, or a role without create-database permission.
psql -h localhost -U postgres -c "SELECT version();"The install
npx create-medusa-app@latest my-storeYou will be asked whether to include the Next.js storefront. Say yes for a first project — having a working storefront to read is worth more than a clean directory.
The installer creates the project, installs dependencies, creates the database, runs migrations, seeds sample data, and prompts for an admin email. When it finishes:
cd my-store
npx medusa developBackend on http://localhost:9000, admin dashboard on http://localhost:9000/app, storefront on http://localhost:8000.
If the automatic setup fails
Common enough to be worth writing down. Do it manually:
# 1. Create the database
createdb medusa-store
# 2. Point the app at it
echo 'DATABASE_URL=postgres://postgres:postgres@localhost:5432/medusa-store' >> .env
# 3. Run migrations
npx medusa db:migrate
# 4. Seed sample data
yarn seed
# 5. Create an admin user
npx medusa user -e admin@example.com -p supersecretThat last command is how admin users are created — there is no self-service signup, by design.
What got created
my-store/
├── src/
│ ├── admin/ # dashboard extensions
│ ├── api/ # custom API routes
│ ├── jobs/ # scheduled jobs
│ ├── links/ # module links
│ ├── modules/ # your commerce domains
│ ├── scripts/ # one-off scripts, including seed
│ ├── subscribers/ # event handlers
│ └── workflows/ # business processes
├── medusa-config.ts # configuration and module registration
└── .envEvery directory is convention-based: put a file in the right place and Medusa loads it. The project structure guide covers what belongs where.
The first-hour configuration
The scaffolded config is fine for development and wrong for anything you intend to keep.
Real secrets. The defaults are placeholders:
JWT_SECRET=$(openssl rand -base64 32)
COOKIE_SECRET=$(openssl rand -base64 32)Restrict CORS. The scaffold allows localhost. Before any deployment, set STORE_CORS, ADMIN_CORS and AUTH_CORS to your real origins.
Add Redis. Even locally it is worth it, because it makes your development environment behave like production:
modules: [
{
resolve: "@medusajs/medusa/event-bus-redis",
options: { redisUrl: process.env.REDIS_URL },
},
]Decide about seed data now. The sample catalog is useful for a day and confusing for a month. Either clear it before you start modelling, or keep it deliberately in a separate database.
Verifying the install
Three checks that together prove the stack works:
# Health
curl http://localhost:9000/health
# Store API — needs a publishable key from the admin dashboard
curl http://localhost:9000/store/products \
-H "x-publishable-api-key: pk_..."
# Admin API — log in first
curl -X POST http://localhost:9000/auth/user/emailpass \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"supersecret"}'The publishable key catches people out: Store API requests require one, it is created in the dashboard under Settings, and the storefront reads it from an environment variable.
What to do next
- Read the project structure so you know where things go.
- Build a small module — a wishlist, a brand — to learn the shape.
- Wire Stripe in test mode and complete an order end to end.
- Read deploying to production before you get attached to a development-only architecture.
Docker Postgres in thirty seconds
If you would rather not install Postgres locally:
docker run --name medusa-pg \
-e POSTGRES_USER=medusa \
-e POSTGRES_PASSWORD=medusa \
-e POSTGRES_DB=medusa \
-p 5432:5432 \
-d postgres:16-alpine
docker run --name medusa-redis -p 6379:6379 -d redis:7-alpineDATABASE_URL=postgres://medusa:medusa@localhost:5432/medusa
REDIS_URL=redis://localhost:6379Two containers, no system packages, and trivially resettable when you want a clean database. For a full local stack see Dockerising Medusa.
Troubleshooting the first hour
| Symptom | Cause | Fix |
|---|---|---|
| `ECONNREFUSED 5432` | Postgres not running | Start it; verify with `psql` |
| `permission denied to create database` | Role lacks createdb | Create the DB manually, then migrate |
| Admin loads but API calls 401 | No admin user | `npx medusa user -e … -p …` |
| Store API returns 400 | Missing publishable key | Create one in Settings, set it in the storefront |
| Storefront shows no products | No sales channel or no key | Link products to the default channel |
| Prices missing | No region context | Pass `region_id` and request `calculated_price` |
| `Cannot find module` after build | Dependencies not installed in `.medusa/server` | Run install inside the build output |
The publishable key and the region context account for most of the confusion in a first project — neither produces an error that names the actual problem.
Upgrading later
Keep the upgrade path in mind from day one, because the habits that make it painless are cheap now and expensive to retrofit.
npx medusa-upgrade # or bump @medusajs/* together, then:
npx medusa db:migrateThree habits that keep upgrades boring: pin exact versions rather than ranges so upgrades are deliberate; bump every @medusajs/* package together, since mixed minor versions produce module resolution errors that name the wrong thing; and read the release notes for migration steps before running db:migrate, not after.
The architectural habit matters more than any of those. Because modules sit beside core rather than extending it, an upgrade rarely touches your code — which is the whole reason v2 was rebuilt this way.
If you would rather skip the learning curve on a project with a deadline, that is what we do.
Frequently asked questions
What are the system requirements for Medusa v2?
Node.js 20 or 22, PostgreSQL 15 or newer, and Git. Redis is optional in development and required in production. Medusa runs on macOS, Linux and Windows via WSL2.
How do I create an admin user in Medusa?
With the CLI: `npx medusa user -e you@example.com -p yourpassword`. There is no signup form in the dashboard, which is deliberate — admin access is provisioned, not self-served.
Why does create-medusa-app fail on database creation?
Almost always because Postgres is not running, or the connecting role lacks permission to create databases. Verify with `psql -h localhost -U postgres -c "SELECT 1;"`, then create the database manually and run `npx medusa db:migrate` yourself.
Do I need the Next.js storefront?
No — Medusa exposes a Store API you can build against with any framework. For a first project the starter is worth including, because reading a working implementation of cart and checkout teaches the API faster than the reference does.
What is a publishable API key?
A key that scopes Store API requests to specific sales channels. Every storefront request must send it as the `x-publishable-api-key` header. Create it in the dashboard under Settings and put it in your storefront's environment.
How do I remove the seed data?
Re-create the database and run migrations without seeding: `npx medusa db:migrate` on a fresh database. Deleting seeded records individually through the dashboard works but tends to leave regions, sales channels and stock locations behind.
Medusa Database Migrations: Generating, Reviewing and Deploying Safely
Generated migrations are only as safe as your review of them. How Medusa migrations work, and the expand-and-contract pattern that keeps rolling deploys from failing.
Deploying Medusa to Production: Architecture, Checklist and Pitfalls
What a production Medusa deployment actually needs — process separation, Redis, migrations, health checks — and the four mistakes that cause the first outage.
Observability for Medusa: Logs, Traces and the Alerts Worth Having
Self-hosting means owning the question 'is checkout working?'. Structured logging, tracing, the four metrics that matter and alerts that do not cry wolf.


