E-commerce platform untuk Metro Apparel, dibangun dengan Medusa (backend) dan Next.js (storefront).
- Backend: Medusa v2 (Node.js)
- Storefront: Next.js 16 dengan React 19
- Database: PostgreSQL 15
- Cache: Redis 7
- Package Manager: pnpm v10 dengan Turborepo
metroapparel/
├── apps/
│ ├── backend/ # Medusa backend + Admin panel
│ └── storefront/ # Next.js storefront
├── docker/
│ ├── development/ # Docker config untuk development
│ └── production/ # Docker config untuk production
└── package.json # Root workspace config
- Clone repository:
git clone <repository-url>
cd metroapparel- Jalankan semua services (PostgreSQL, Redis, Medusa):
pnpm docker:up- Buat admin user:
docker compose exec medusa sh -c "cd /server/apps/backend && pnpm medusa user -e admin@example.com -p supersecret"-
Akses admin panel di
http://localhost:9000/appdan login. Ambil Publishable API Key di Settings > Publishable API Keys. -
Update
apps/storefront/.env:
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_xxxxx- Jalankan storefront:
docker compose up storefront -d- Akses storefront di
http://localhost:8000
| Command | Description |
|---|---|
pnpm docker:up |
Start semua services |
pnpm docker:down |
Stop semua services |
pnpm docker:logs |
View logs |
Panduan deployment ke VPS dengan Traefik sebagai reverse proxy dan SSL termination.
Internet → Traefik (HTTPS/SSL)
├── admin.yourdomain.com → Medusa Backend (port 9000)
└── store.yourdomain.com → Next.js Storefront (port 8000)
- VPS dengan Docker dan Docker Compose
- Traefik sudah terinstall dan running
- Domain dengan DNS yang sudah dikonfigurasi
Buat file docker-compose.yml untuk Traefik:
version: "3.9"
services:
traefik:
image: traefik:latest
container_name: traefik
command:
- "--providers.docker=true"
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
- "--providers.docker.exposedbydefault=false"
- "--api.dashboard=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./acme.json:/acme.json"
networks:
- traefik_network
networks:
traefik_network:
external: trueBuat network dan file acme.json:
docker network create traefik_network
touch acme.json && chmod 600 acme.json
docker compose up -dTambahkan A records di DNS provider:
| Type | Name | Value |
|---|---|---|
| A | admin.metroapparel | <IP_VPS> |
| A | metroapparel | <IP_VPS> |
cd ~
git clone <repository-url> metroapparel
cd metroapparelBuat file .env.production di root project:
# Database
POSTGRES_USER=postgres
POSTGRES_PASSWORD=<PASSWORD_KUAT>
# Medusa secrets (generate dengan: openssl rand -hex 32)
JWT_SECRET=<HASIL_GENERATE>
COOKIE_SECRET=<HASIL_GENERATE>
# Publishable Key (diisi setelah deploy Medusa)
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=Generate secrets:
openssl rand -hex 32 # Jalankan 2x untuk JWT_SECRET dan COOKIE_SECRETEdit docker/production/docker-compose.yml dan ganti domain sesuai kebutuhan:
admin.metroapparel.web.id→ domain admin kamumetroapparel.web.id→ domain storefront kamu
Update juga environment variables STORE_CORS, ADMIN_CORS, AUTH_CORS, NEXT_PUBLIC_MEDUSA_BACKEND_URL, dan NEXT_PUBLIC_BASE_URL.
PENTING: Hapus semua file .env*.local di folder apps sebelum deploy. File-file ini bisa meng-override environment variables production dan menyebabkan build gagal.
cd ~/metroapparel
# Hapus local env files yang bisa mengganggu production build
rm -f apps/storefront/.env.local
rm -f apps/storefront/.env.production.local
rm -f apps/backend/.env.local
rm -f apps/backend/.env.production.localcd ~/metroapparel
# Start database dan redis
docker compose --env-file .env.production -f docker/production/docker-compose.yml up -d postgres redis
# Tunggu postgres ready
sleep 10
# Build dan start Medusa
docker compose --env-file .env.production -f docker/production/docker-compose.yml up -d medusa --build
# Monitor logs
docker compose --env-file .env.production -f docker/production/docker-compose.yml logs -f medusaTunggu sampai muncul log Server is ready on port: 9000.
Note: Selalu gunakan flag
--env-file .env.productionuntuk memastikan environment variables terload dengan benar, terutama saat menggunakansudo.
docker compose --env-file .env.production -f docker/production/docker-compose.yml exec medusa \
pnpm medusa user -e admin@yourdomain.com -p <PASSWORD_ADMIN>- Akses
https://admin.yourdomain.com/app - Login dengan credentials yang dibuat
- Pergi ke Settings → API Key Management → Publishable API Keys
- Copy publishable key
nano .env.production
# Paste publishable key ke:
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_xxxxxcd ~/metroapparel
# Build dan start storefront
docker compose --env-file .env.production -f docker/production/docker-compose.yml up -d storefront --build
# Monitor logs
docker compose --env-file .env.production -f docker/production/docker-compose.yml logs -f storefrontTunggu sampai muncul log Ready in Xms.
# Cek semua services running
docker compose --env-file .env.production -f docker/production/docker-compose.yml ps
# Test endpoints
curl -I https://admin.yourdomain.com/health
curl -I https://yourdomain.comSemua perintah menggunakan --env-file .env.production untuk memastikan environment variables terload dengan benar.
| Command | Description |
|---|---|
docker compose --env-file .env.production -f docker/production/docker-compose.yml up -d |
Start semua services |
docker compose --env-file .env.production -f docker/production/docker-compose.yml down |
Stop semua services |
docker compose --env-file .env.production -f docker/production/docker-compose.yml logs -f |
View all logs |
docker compose --env-file .env.production -f docker/production/docker-compose.yml logs -f medusa |
View Medusa logs |
docker compose --env-file .env.production -f docker/production/docker-compose.yml logs -f storefront |
View Storefront logs |
docker compose --env-file .env.production -f docker/production/docker-compose.yml up -d --build medusa |
Rebuild Medusa |
docker compose --env-file .env.production -f docker/production/docker-compose.yml up -d --build storefront |
Rebuild Storefront |
docker compose --env-file .env.production -f docker/production/docker-compose.yml exec medusa sh |
Shell ke Medusa container |
Penyebab: Traefik tidak bisa connect ke container.
Solusi:
- Pastikan container running:
docker ps - Cek apakah container di network yang benar:
docker network inspect traefik_network
- Pastikan label
traefik.docker.network=traefik_networkada di docker-compose
Penyebab: File .env.production.local atau .env.local di folder apps/storefront/ meng-override environment variables production saat build Next.js.
Solusi:
- Hapus file local env yang mengganggu:
rm -f apps/storefront/.env.local rm -f apps/storefront/.env.production.local
- Rebuild storefront:
docker compose --env-file .env.production -f docker/production/docker-compose.yml up -d storefront --build --force-recreate
Penyebab: Perintah export $(grep ...) tidak bekerja dengan sudo karena sudo tidak preserve environment variables user.
Solusi: Selalu gunakan flag --env-file:
docker compose --env-file .env.production -f docker/production/docker-compose.yml up -dPenyebab: Traefik menggunakan IP dari network yang salah (container punya multiple networks).
Solusi: Tambahkan label di docker-compose:
labels:
- "traefik.docker.network=traefik_network"Penyebab: DNS belum propagate atau port 80 tidak accessible.
Solusi:
- Cek DNS propagation:
dig yourdomain.com - Pastikan port 80 dan 443 open di firewall
- Cek Traefik logs:
docker logs traefik
Penyebab: Environment variable salah atau network issue.
Solusi:
- Pastikan
MEDUSA_BACKEND_INTERNAL_URL=http://medusa:9000di storefront - Test koneksi:
docker exec metroapparel_storefront wget -qO- http://medusa:9000/health
Penyebab: Medusa tidak dikonfigurasi dengan backendUrl, sehingga URL file uploads menggunakan default http://localhost:9000.
Solusi: Pastikan environment variable MEDUSA_BACKEND_URL diset di Medusa container:
environment:
- MEDUSA_BACKEND_URL=https://admin.yourdomain.comDan pastikan medusa-config.ts menggunakan variabel tersebut:
projectConfig: {
backendUrl: process.env.MEDUSA_BACKEND_URL,
// ...
}Setelah perubahan, rebuild Medusa:
docker compose --env-file .env.production -f docker/production/docker-compose.yml up -d medusa --build --force-recreatePenyebab: pnpm workspace symlinks tidak bekerja di Docker.
Solusi: Gunakan Next.js standalone output (sudah dikonfigurasi di next.config.ts):
const nextConfig: NextConfig = {
output: 'standalone',
// ...
};| Variable | Description | Required |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Yes |
REDIS_URL |
Redis connection string | Yes |
JWT_SECRET |
Secret untuk JWT tokens | Yes |
COOKIE_SECRET |
Secret untuk cookies | Yes |
MEDUSA_BACKEND_URL |
URL publik Medusa untuk generate URL file uploads | Yes (production) |
STORE_CORS |
Allowed origins untuk storefront | Yes |
ADMIN_CORS |
Allowed origins untuk admin panel | Yes |
AUTH_CORS |
Allowed origins untuk authentication | Yes |
| Variable | Description | Required |
|---|---|---|
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY |
Publishable API key dari Medusa | Yes |
NEXT_PUBLIC_MEDUSA_BACKEND_URL |
URL Medusa untuk browser | Yes |
MEDUSA_BACKEND_INTERNAL_URL |
URL Medusa untuk SSR (internal Docker) | Yes |
NEXT_PUBLIC_BASE_URL |
Base URL storefront | Yes |
NEXT_PUBLIC_DEFAULT_REGION |
Default region code | No |
PORT |
Port untuk Next.js server | No |