Medusa on VPS: Installing the Self-Hosted Shopify Alternative on Node.js

31.03.2026
12:27

Medusa positions itself as an open-source Shopify alternative and has earned that audience: over 20,000 GitHub stars and thousands of production stores. The difference from Shopify is fundamental: Shopify is a SaaS at $29–299/month with transaction fees and limited customization. Medusa is an open-source platform you deploy on your own server, pay only for hosting, and modify however you need.

Current version: Medusa v2. Built on Node.js and TypeScript, runs on port 9000, uses PostgreSQL as the primary database and Redis for background tasks. License: MIT — the most permissive open-source license, no restrictions on commercial use.

This guide covers who Medusa is for, how to install it on THE.Hosting VPS in any of 50+ locations — from Germany (Frankfurt) to Japan (Tokyo) — and how to run it in production with PostgreSQL, Redis, PM2, and Nginx.

What Medusa Is and How It Differs from Shopify

The Shopify comparison is the fastest way to understand what Medusa does and doesn't do.

Shopify is a monolith: storefront, hosting, payment processing, CDN — all bundled in one SaaS. Fast to launch, no developer required, but every non-standard requirement turns into a workaround or an expensive app. Plus transaction fees on every order.

Medusa is an API-first framework: the backend exposes a REST API and GraphQL for all store operations, and the frontend is built separately. No restrictions on customizing checkout, pricing logic, or integrations with any payment provider. Medusa charges zero transaction fees.

Out of the box: product and catalog management, cart and checkout, order management, customers and order history, discounts and promo codes, multi-region support with per-region currencies and taxes, inventory management. The admin dashboard is accessible at /app on the same server.

Medusa is a developer tool. Managing a store through the UI without code is possible, but the customer-facing storefront has to be built separately — or via the official Next.js Starter Storefront.

System Requirements

  • Node.js v20 or higher (v20, v22, v24 — officially tested)
  • PostgreSQL 10+ (primary and recommended database)
  • Redis 6+ (for background tasks and the worker)
  • Minimum 2 GB RAM — Medusa's official production recommendation
  • 10+ GB disk space

Note on Next.js Starter Storefront: if you plan to install it alongside the backend, Node.js v24 LTS is the maximum. Version v25+ is incompatible with the starter.

Preparing the VPS

Connect:

ssh root@your-IP-address

Update the system:

apt update && apt upgrade -y
apt install -y curl wget git build-essential

Create a working user:

adduser medusa
usermod -aG sudo medusa
su - medusa

Firewall:

ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 9000/tcp   # temporary, for initial verification
ufw enable

Timezone:

timedatectl set-timezone America/New_York

Installing Node.js 22

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install 22
nvm use 22
nvm alias default 22
node --version    # v22.x.x

Installing PostgreSQL

sudo apt install postgresql postgresql-contrib -y
sudo systemctl start postgresql
sudo systemctl enable postgresql

Create the database and user:

sudo -u postgres psql
CREATE DATABASE medusa_db;
CREATE USER medusa_user WITH PASSWORD 'strong_password';
GRANT ALL PRIVILEGES ON DATABASE medusa_db TO medusa_user;
\c medusa_db
GRANT ALL ON SCHEMA public TO medusa_user;
\q

Installing Redis

sudo apt install redis-server -y
sudo systemctl start redis-server
sudo systemctl enable redis-server
redis-cli ping    # PONG

Creating the Medusa Project

Use the official CLI:

cd ~
npx create-medusa-app@latest my-store

The CLI asks a couple of questions:

  • What's the name of your project? — project name (or press Enter for my-store)
  • Would you like to install the Next.js Starter Storefront? — Yes for a storefront out of the box; No for backend only

The CLI connects to your local PostgreSQL, creates the database, runs migrations, and seeds initial data automatically.

After installation the server launches automatically. Open http://your-IP:9000/app in a browser — the dashboard prompts you to create an admin account.

Stop the server (Ctrl+C) and proceed to production configuration.

Environment Configuration

The project root contains a .env file. Open it:

cd ~/my-store
nano .env

For production, set secret keys and confirm the DATABASE_URL points to the right credentials:

DATABASE_URL=postgres://medusa_user:strong_password@localhost:5432/medusa_db
REDIS_URL=redis://localhost:6379
JWT_SECRET=long_random_string_minimum_32_characters
COOKIE_SECRET=another_long_random_string
NODE_ENV=production

Generate random strings for the secrets:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Run this twice — once for JWT_SECRET and once for COOKIE_SECRET.

Production Build

cd ~/my-store
npm run build

This compiles TypeScript and builds the admin dashboard. Takes 1–3 minutes.

PM2 for Production

npm install -g pm2

Create the PM2 config:

nano ~/my-store/ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'medusa-server',
      script: 'npm',
      args: 'run start',
      cwd: '/home/medusa/my-store',
      instances: 1,
      env: {
        NODE_ENV: 'production',
        PORT: 9000,
      },
    },
  ],
};

Start and enable autostart:

cd ~/my-store
pm2 start ecosystem.config.js
pm2 save
pm2 startup
# copy and run the command that pm2 startup outputs

Verify:

pm2 status
pm2 logs medusa-server

You should see: Server is ready on port: 9000.

Nginx as Reverse Proxy

sudo apt install nginx -y
sudo nano /etc/nginx/sites-available/medusa

Content:

server {
    listen 80;
    server_name api.yourstore.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name api.yourstore.com;

    ssl_certificate /etc/letsencrypt/live/api.yourstore.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.yourstore.com/privkey.pem;

    client_max_body_size 50M;

    location / {
        proxy_pass http://localhost:9000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}
sudo ln -s /etc/nginx/sites-available/medusa /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

SSL certificate:

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d api.yourstore.com

After — close direct port access:

ufw delete allow 9000/tcp

CORS Configuration

Medusa blocks cross-origin requests by default. In medusa-config.ts specify the domains of your storefront and dashboard:

module.exports = defineConfig({
  projectConfig: {
    http: {
      storeCors: process.env.STORE_CORS || 'https://yourstore.com',
      adminCors: process.env.ADMIN_CORS || 'https://api.yourstore.com',
      authCors: process.env.AUTH_CORS || 'https://yourstore.com',
    },
    databaseUrl: process.env.DATABASE_URL,
    redisUrl: process.env.REDIS_URL,
  },
});

Add the corresponding variables to .env:

STORE_CORS=https://yourstore.com
ADMIN_CORS=https://api.yourstore.com
AUTH_CORS=https://yourstore.com

Choosing a VPS at THE.Hosting

Production minimum: Medusa's official recommendation is 2 GB RAM. Go with Standard VPS at 2 vCPU / 4 GB RAM / 40 GB NVMe — comfortable headroom for PostgreSQL and Redis alongside the Node.js process.

For growth: 4 vCPU / 8 GB RAM for high traffic or multiple active regions with inventory sync.

European market: Germany (Frankfurt) — GDPR, financial hub; Netherlands (Meppel) — best connectivity; UK (London) — for UK-focused stores; France (Paris) — French-speaking audience.

American audience: USA (New Jersey, Secaucus) — East Coast; Canada (Toronto); Brazil (São Paulo) — Latin America's largest market.

Asian market: Japan (Tokyo) — lowest latency for East Asia; Hong Kong — gateway to China; South Korea (Seoul).

CIS: Moldova (Chișinău) — Dedicated servers; Finland (Helsinki).

Medusa scales horizontally: the worker can be moved to a separate VPS, PostgreSQL to a dedicated server. A single 4 GB RAM VPS covers most starting scenarios.

Common Issues

PostgreSQL connection error. Verify the password and database name in DATABASE_URL. For PostgreSQL 15+, explicitly grant schema rights: GRANT ALL ON SCHEMA public TO medusa_user; in psql.

CORS errors when opening the dashboard. The exact domain opening the dashboard — including protocol and non-standard ports — must be in adminCors in medusa-config.ts.

PM2 can't find npm or the script. Confirm cwd holds an absolute path to the project, and that Node.js is in PATH for the user running PM2. If installed via nvm, the path may need to be added to ~/.bashrc.

medusa requires at least v20 of Node.js even on v22. A known issue when multiple Node.js versions coexist. Check: which node and node --version. If the path doesn't match nvm — restart the terminal or run nvm use 22.

Build crashes on low-memory VPS. On a 1 GB RAM VPS, npm run build can run out of memory compiling TypeScript. Increase the limit: NODE_OPTIONS=--max-old-space-size=1024 npm run build.

Ready to launch your own Shopify alternative?

On THE.Hosting VPS with NVMe drives and root access you get full control: no transaction fees, no customization limits. 50+ locations — choose the one closest to your audience.

Order VPS for Medusa

FAQ:

Is Medusa free? The open-source core under the MIT license is completely free, including commercial use. Medusa Inc also offers a managed cloud (Medusa Cloud) starting at $299/month for those who don't want to maintain infrastructure themselves. Self-hosted on a VPS costs nothing beyond the hosting.

Is a separate frontend required? Yes. Medusa provides an API and admin dashboard, but there's no customer-facing storefront out of the box. The official Next.js Starter Storefront can be installed during project creation — it's a ready storefront you connect to your Medusa backend.

How does Medusa differ from Vendure? Both are Node.js/TypeScript API-first platforms. Medusa positions itself more actively as a Shopify replacement and has more mature support for multi-region selling, complex promotions, and inventory management out of the box. Vendure is built on NestJS with GraphQL; Medusa uses REST API with optional GraphQL. Medusa has 20,000+ GitHub stars and a wider plugin ecosystem.

Does it work with any payment provider? Yes. Medusa supports Stripe, PayPal, Klarna, Mollie, Braintree, and others through official and community plugins. You can also write a custom payment provider for any system — the API is open.

Can I migrate from Shopify to Medusa? Technically yes — by importing products, customers, and orders into PostgreSQL. There's no official automated migrator, but the community maintains several tools. Migration complexity depends on how many Shopify customizations you have.

THE.Hosting Useful Links:

Medusa Resources:

Other articles

31.03.2026
3
Knowledge base / All about domains
.MOBI Domain Zone