How to Set Up a CI/CD Pipeline for Your Medusa.js Store with GitHub Actions

    Shipping code manually to production is a gamble. One missed environment variable, one untested route, and your store goes down at the worst possible moment. For teams building on Medusa.js, automating the deploy process with GitHub Actions is not just a convenience. It is a foundation for shipping reliably, confidently, and fast.

    This guide walks you through setting up a full CI/CD pipeline for your Medusa.js store using GitHub Actions. Whether you are running a solo project or coordinating across a team, the workflow patterns here will save you hours and reduce deployment risk significantly.

    Why CI/CD Matters for Medusa.js Projects

    Medusa.js is modular and highly configurable. That flexibility is its greatest strength, but it also means more moving parts during deployment. A custom module, a new payment integration, or a storefront update can introduce regressions that only show up in production.

    A CI/CD pipeline gives your team a structured path from code commit to live production. Every push gets tested, validated, and deployed through a consistent process. You can read about Medusa's modular architecture in depth at the official Medusa.js documentation to understand why deployment consistency matters as your customisations grow.

    Ready to automate your Medusa.js deployments?

    Lets Talk

    What You Need Before You Begin

    Before writing a single workflow file, get the following in place:

    • A Medusa.js v2 project pushed to a GitHub repository

    • A deployment target (Railway, Render, Fly.io, or a VPS with SSH access)

    • Environment variables stored in GitHub Secrets

    • Node.js 20 or above as your runtime

    • A PostgreSQL database and Redis instance accessible from your deployment environment

    Understanding the Medusa.js Deploy Process

    Deploying a Medusa.js store involves more than copying files to a server. You need to run database migrations, seed any required data, build the admin dashboard, and restart the Node.js process. GitHub Actions lets you automate each of these steps in a defined order.

    Setting Up Your GitHub Actions Workflow File

    Create a directory at .github/workflows/ in your repository root. Inside it, add a file named deploy.yml. This is where your entire CI/CD logic lives.

    A clean starting structure for your workflow file should define three things: the trigger events, the environment, and the job steps. For most Medusa.js projects, you will want the pipeline to run on every push to your main branch and on every pull request to catch regressions before they merge.

    Defining Triggers

    Your workflow should trigger on pushes to main for deployment and on pull requests for testing. This separation keeps your production deploys deliberate while still running tests automatically on every code review. Using the on: push condition scoped to the main branch prevents feature branches from accidentally triggering production deploys.

    Configuring the Environment

    GitHub Actions runs your jobs on hosted runners. For a Medusa.js project, ubuntu-latest works well. Specify your Node.js version explicitly using the actions/setup-node action to avoid version drift between local development and CI. Locking to Node 20 matches Medusa v2's recommended runtime.

    Need a custom CI/CD setup for Medusa.js?

    Lets Talk

    Storing Secrets and Environment Variables

    Never hardcode database credentials, JWT secrets, or API keys in your workflow file. GitHub provides an encrypted Secrets store accessible from Settings > Secrets and variables > Actions.

    For a Medusa.js project, the minimum secrets you need are your database connection string, Redis URL, JWT secret, and cookie secret. You reference these in your workflow as context expressions, and GitHub injects the values at runtime. Your secrets never appear in logs or workflow outputs.

    Writing the Test Job

    Before anything reaches production, your pipeline should validate the codebase. The test job should install dependencies with npm ci, set up a test database using PostgreSQL as a service container within GitHub Actions, and run your test suite.

    GitHub Actions supports service containers for PostgreSQL and Redis natively. You can spin up both inside your workflow without needing an external database, which keeps your tests isolated and fast. Medusa.js's module system makes unit testing individual modules straightforward, and you should aim to test your custom modules independently before integration tests run.

    The Medusa.js community maintains active testing patterns and examples on the Medusa GitHub repository, which is worth reviewing when structuring your test suite.

    Writing the Build Job

    Once tests pass, your build job compiles the Medusa backend and the storefront. For a Next.js storefront consuming Medusa's APIs, the build step runs next build. For the Medusa backend itself, running medusa build generates the optimised server output.

    A common mistake is caching node_modules improperly. Use GitHub's actions/cache with a key based on your package-lock.json hash. This avoids reinstalling hundreds of packages on every run while still invalidating the cache when your dependencies actually change.

    Writing the Deploy Job

    The deploy job runs only after the test and build jobs succeed. You can enforce this with the needs: [test, build] directive in your workflow. This sequencing ensures that a failing test blocks deployment automatically, without any manual intervention.

    For deployment, your approach depends on your hosting platform.

    Deploying to Railway or Render

    Both Railway and Render support webhook-triggered deploys. You generate a deploy hook URL in your platform dashboard and call it from your GitHub Actions workflow using a curl command. This is the simplest approach and works well for smaller teams. The platform handles container rebuilds, migration runs, and restarts on its own after receiving the webhook.

    Deploying to a VPS via SSH

    For teams running their own servers, you can deploy via SSH from GitHub Actions using the appleboy/ssh-action. You store your server's SSH private key as a GitHub Secret, and your workflow connects to the server, pulls the latest code, runs npm ci and medusa migrations run, then restarts the process with PM2.

    Automate your Medusa.js store today

    Lets Talk

    Running Database Migrations Safely

    Database migrations deserve careful handling in your pipeline. Running them inside your deploy job, before the server restarts, ensures your schema is always in sync with your application code. The command medusa migrations run applies any pending migrations and is idempotent, meaning it is safe to run even when there are no pending changes.

    For zero-downtime deployments, consider running migrations as a separate step before swapping traffic to the new version. This pattern works especially well when using blue-green deployment setups or platforms that support traffic routing controls.

    Handling Multi-Environment Pipelines

    Most production Medusa.js stores benefit from a staging environment that mirrors production. You can configure separate workflow jobs for staging and production, triggered by different branches or tags. A push to the develop branch deploys to staging, while a push to main or a tagged release deploys to production.

    Keeping staging and production as close as possible reduces the chance of deployment surprises. Use the same GitHub Actions runner, the same Node.js version, and the same deploy process. Only the secrets and the deployment targets should differ.

    Monitoring Your Pipeline

    Once your pipeline is live, visibility into its health matters. GitHub Actions provides a built-in dashboard showing run history, step durations, and failure details. For longer pipelines, notifications via Slack or email on failure keep your team informed without requiring them to manually check every run.

    Track your pipeline metrics over time. If builds consistently take longer than five minutes, look at your caching strategy first. Slow installs and uncached builds are the most common bottleneck in Medusa.js CI pipelines.

    Common Mistakes to Avoid

    • Running migrations after the server restarts instead of before, which can cause schema mismatch errors on startup

    • Not pinning your Node.js version in the workflow, leading to unexpected breakage when GitHub updates runner images

    • Storing secrets in your workflow file directly instead of in GitHub's encrypted Secrets store

    • Skipping the needs: directive, which allows a broken build to proceed to deployment

    • Rebuilding node_modules from scratch on every run when caching would cut build times significantly

    Ready to Transform
    Your Business?

    Build your next landing page fast & easy

    Available now

    Free consultation included. We'll review your requirements and provide a detailed proposal.