Deploying Medusa to Oracle Cloud
Tags: medusajs, selfhost, ecommerce, webdev
Published: 2023-08-28 11:36:01

## How to Deploy a Medusa Commerce Backend Server using Oracle Cloud Compute Infrastructure

## Introduction

[Medusa](https://medusajs.com/readme/) is a modular commerce engine that helps developers build rich digital commerce applications. It is free, open source, and uses Node.js. With Medusa, a developer can quickly build advanced commerce solutions without coding from scratch.

Medusa is very flexible and customizable. It can be hosted in the cloud using IAAS and PAAS.

[Oracle Cloud](https://www.oracle.com/cloud/) provides virtual machines suitable enough to host a Medusa backend.

This guide will teach you how to deploy a live server of the Medusa backend in the cloud using OCI.

## Why Oracle Cloud?

Oracle Cloud offers a very generous "Always-Free" tier for its cloud services. You can host the Medusa server application within the OCI all for free.

The free services of interest that OCI provides to your app are:
- An ARM-based VM with:
    - Upto 8 CPUs
    - Linux
    - 24GB of memory
    - 50GB boot volume for storage
- Up to  200GB of block storage for your VM with 5 backups
- Free Load Balancer with max bandwidth set to 10Mbps
- Up to 10TB per month of Outbound Data Transfer
- Unlimited Inbound Data Transfer

For more information about the all the free services offered Oracle Cloud check out the [Always Free Resources](https://docs.oracle.com/en-us/iaas/Content/FreeTier/freetier_topic-Always_Free_Resources.htm) page.

## Prerequisites

- A GitHub account. [Sign up for one here]()
- An Oracle Cloud account. [Sign up for Oracle Cloud account here](https://signup.oraclecloud.com)

## Create OCI Linux Instance

### Create a compartment for your resources

In this step, you will create a compartment to control and organize the resources you will use for your Medusa backend server. You will only need one compartment to manage all the project resources.

[Sign in](https://docs.oracle.com/en-us/iaas/Content/GSG/Tasks/signingin.htm#Signing_In_to_the_Console) to your Oracle Cloud Console.


![Oracle Cloud Sign In](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s8nl5fdgbytwfk4u04ne.png)

Click on the navigation menu icon and select **Identity & Security**. Choose **Compartments** under the **Identity** option.

![Select Compartments on Navigation Menu](https://res.cloudinary.com/craigsims808/image/upload/v1684746914/articles/oracle-medusa/select-compartments-on-nav-menu_kdcijv.png)

Click **Create Compartment**.

Enter the following:
- **Name:** Enter "Medusa"
- **Description:** Enter "Compartment for Medusa backend server"
- **Parent Compartment:** Select the default root compartment

![Click Create Compartment](https://res.cloudinary.com/craigsims808/image/upload/v1684746912/articles/oracle-medusa/create-compartment_ev0cqw.png)

Select **Create Compartment**, wait a moment, and refresh to see your compartment displayed in the list of compartments.

![List of Compartments](https://res.cloudinary.com/craigsims808/image/upload/v1684746913/articles/oracle-medusa/list-of-compartments_tjoqyd.png)

Click on **Networking** in the navigation menu and select **Virtual cloud networks**.

![Select Virtual Cloud Networks](https://res.cloudinary.com/craigsims808/image/upload/v1684746915/articles/oracle-medusa/select-vcn-nav-menu_josftr.png)

From the compartment list on the left, select the **Medusa** compartment you created before.

![Select Medusa Compartment](https://res.cloudinary.com/craigsims808/image/upload/v1684746915/articles/oracle-medusa/select-medusa-compartment_dpvs7g.png)

> *For more information check out these [instructions on Choosing and Creating a Compartment for your project](https://docs.oracle.com/en-us/iaas/Content/GSG/Tasks/choosingcompartments.htm#Choosing_a_Compartment).*

### Create a virtual cloud network

Click the **Start VCN Wizard** button.

![Select Start VCN Wizard](https://res.cloudinary.com/craigsims808/image/upload/v1684746914/articles/oracle-medusa/select-start-vcn-wizard_z2kp8l.png)

Choose the **Create VCN with Internet Connectivity** option, and then click **Start VCN Wizard**

![Create VCN with Internet Connectivity](https://res.cloudinary.com/craigsims808/image/upload/v1684746912/articles/oracle-medusa/create-vcn-with-internet-connectivity_zquzqm.png)

Enter these details:
- **VCN Name**: medusa
- **Compartment**: Select the Medusa compartment
- **VCN CIDR Block**: 10.0.0.0/16
- **Public Subnet CIDR Block**: 10.0.0.0/24
- **Private Subnet CIDR Block**: 10.0.1.0/24

> *Leave all the other details as they are.*

![Details for VCN](https://res.cloudinary.com/craigsims808/image/upload/v1684746913/articles/oracle-medusa/details-for-vcn_yofjhe.png)

Click **Next** and review the details for your VCN and click on **Create**. Wait a few seconds for your VCN to be created, then select **View VCN**.

![View Virtual Cloud Network](https://res.cloudinary.com/craigsims808/image/upload/v1684746915/articles/oracle-medusa/view-virtual-cloud-network_xjkodl.png)

> *For more information check out [Creating a Virtual Cloud Network](https://docs.oracle.com/en-us/iaas/Content/GSG/Tasks/creatingnetwork.htm#Creating_a_Virtual_Cloud_Network)*

### Launch an instance

Select **Instances** under **Compute** in the navigation menu of your Oracle Dashboard.

![select instances in nav menu](https://res.cloudinary.com/craigsims808/image/upload/v1684746914/articles/oracle-medusa/select-instances-in-nav-menu_eec1da.png)

Click **Create Instance** to take you to the **Create compute instance** page.

![create instance](https://res.cloudinary.com/craigsims808/image/upload/v1684746913/articles/oracle-medusa/click-create-instance_rrlnh2.png)

Enter the name `medusa-server` for your instance and the compartment should be `Medusa`.

![name and  compartment](https://res.cloudinary.com/craigsims808/image/upload/v1684746913/articles/oracle-medusa/name-and-compartment_sr5rlv.png)

For the **Placement** section, leave the default **Availability domain** as it is.

Choose the following **Image** and **shape**:
- **Image:** `Ubuntu 22.04`
- **Shape:** `VM.Standard.A1.Flex`

> *If you need a different image and shape for your `medusa-server` VM, you can follow these [instructions](https://docs.oracle.com/en-us/iaas/Content/GSG/Tasks/launchinginstance.htm#Launching_a_Linux_Instance:~:text=In%20the%20Image%20and%20shape%20section%2C%20make%20the%20following%20selections%3A) to change them.*

![final image and shape](https://res.cloudinary.com/craigsims808/image/upload/v1684746912/articles/oracle-medusa/change-image-and-shape_jxc5ag.png)

For the **Networking** section, add the following configurations:
- **Primary network**: `Select existing virtual cloud network`
- **Virtual cloud network in `medusa`**: `medusa`
- **Subnet**: `Select existing subnet`
- **Subnet in `medusa`**: `public subnet-medusa (regional)`
- **Public IPv4 address**: **Assign a public IPv4 address**

![networking section](https://res.cloudinary.com/craigsims808/image/upload/v1684746913/articles/oracle-medusa/networking-options_qbjrm4.png)

In the **Add SSH key** section:

- Choose **Generate a key pair for me**

![generate key pair](https://res.cloudinary.com/craigsims808/image/upload/v1684746913/articles/oracle-medusa/generate-key-pair_yo3p2c.png)

OCI will generate an SSH key for your instance

- Select **Save Private Key** to save the private key in your computer as a `.key` file.

![save private key](https://res.cloudinary.com/craigsims808/image/upload/v1684746914/articles/oracle-medusa/save-private-key_tybj36.png)

> *If you prefer other options for establishing an SSH connection use [these instructions](https://docs.oracle.com/en-us/iaas/Content/GSG/Tasks/launchinginstance.htm#Launching_a_Linux_Instance:~:text=Upload%20public%20key%20files%20(.pub))*

For the **Boot volume** section, leave the default options.

Click **Create** and wait for your instance to be provisioned.

![click create](https://res.cloudinary.com/craigsims808/image/upload/v1684746912/articles/oracle-medusa/click-create_sjiglv.png)

Wait for a few minutes for your instance to be created and you will see it the Console. When the status of the instance has changed to `RUNNING` you can now connect to it.

![vm running](https://res.cloudinary.com/craigsims808/image/upload/v1684755320/articles/oracle-medusa/vm_running_zz5gcg.png)

> *For more information check out [Launching a Linux Instance](https://docs.oracle.com/en-us/iaas/Content/GSG/Tasks/launchinginstance.htm#Launching_a_Linux_Instance) in the Oracle Cloud Documentation.*

### Connect to the Instance

You will use the private key generated in the previous section to connect to the instance.

> *If you are using a Windows system you must install OpenSSH. If you don't have it, download and install the [PuTTY Key Generator](https://www.putty.org/)*

Note down the **Public IP address** and **Username** for your running instance.

![ip address for instance](https://res.cloudinary.com/craigsims808/image/upload/v1684755320/articles/oracle-medusa/vm_details_amrggp.png)

To apply the appropriate permissions to your SSH private key so that you and only you can read it, follow these instructions for [Unix-style systems](https://docs.oracle.com/en-us/iaas/Content/GSG/Tasks/testingconnection.htm#connecting__linux-from-unix) and these for [Windows System with OpenSSH](https://docs.oracle.com/en-us/iaas/Content/GSG/Tasks/testingconnection.htm#connecting__linux-from-windows-openssh).

After applying permissions to your private key, open up a new terminal session on your local computer and use the following SSH command to access the Oracle instance:

Where:

- `<private_key_file>` is the path and name of the private SSH key file in your computer associated with your medusa server instance.
- `<username>` is the default username you noted down in the previous step. In this case it is `ubuntu`.
- `<public-ip-address>` is the IP address for your instance you noted down in a previous step.

If your SSH connection is successful, you should be logged in to your medusa server instance as the default `ubuntu` user.

![successful ssh login](https://res.cloudinary.com/craigsims808/image/upload/v1684755320/articles/oracle-medusa/successful-ssh-connection_w7ofcz.png)

## Set up and Configure Environment

### Install Node.js and Set up Git

To deploy a Medusa server into an OCI, you must have Node v16 or greater. 

Ensure you are still logged in to your server instance and run the following commands:

```bash
curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install nodejs
node -v && npm -v
```

The last command should give you v18.x.x for node and 9.x.x for npm to confirm successful installation.

If you see errors when trying to install packages globally, [resolve the access permissions](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally) for npm using the following command:

```bash
mkdir ~/.npm-global && npm config set prefix '~/.npm-global' && echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.profile && source ~/.profile
```

Git comes by default in Ubuntu. You can confirm if it is installed by running the following command:

```bash
git --version
```
If it is not installed follow these instructions on [how to install Git on Ubuntu 22.04](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)

Configure the global `username` and `email` for your [Git Identity](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup).

### Set up and configure PostgreSQL Database.

When deploying Medusa, it is recommended to use a PostgreSQL database. In this step, you will install PostgreSQL and create a database for your Medusa server.

To download and install PostgreSQL run the following commands:

```bash
sudo sh -c \
  'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - \
  https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get -y install postgresql
```

To check if the PostgreSQL installation was successful, run the following:

```bash
psql -V
```

Access the PostgreSQL console to create a new user and database for the Medusa server.

```bash
sudo -u postgres psql
```

To create a new user named `medusa_admin` run this command:

```sql
CREATE USER medusa_admin WITH PASSWORD 'medusa_admin_password';
```

Now, create a new database named `medusa_db` and make `medusa_admin` the owner.

```sql
CREATE DATABASE medusa_db OWNER medusa_admin;
```

Last, grant all privileges to `medusa_admin` and exit the PostgreSQL console.

```sql
GRANT ALL PRIVILEGES ON DATABASE medusa_db TO medusa_admin;
```

```sql
exit
```

## Set up a Medusa app on your local machine

In your web browser, [sign in to your GitHub account](https://github.com/) and create a [GitHub repository](https://github.com/new) to store your Medusa server app.

![Create Medusa server GitHub repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mo9okg816bgu0z6375bk.png)

In a new terminal session, [clone](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) the repo you have just created to your local machine and change to the new directory:

```bash
git clone https://github.com/<username>/<reponame>.git
cd <reponame>
```

> *`<username>` refers to your GitHub username.*
> *`<reponame>` refers to your GitHub repo.*

Set up a Medusa server app using the [Medusa Server Quickstart Guide](https://docs.medusajs.com/development/backend/install) instructions.

If you successfully followed the instructions, you should have a directory named `my-medusa-store` containing your Medusa server app and a server running on port `9000`.

Here's the tree structure for the `my-medusa-store` folder.

```bash
.
├── my-medusa-store
│   ├── .babelrc.js
│   ├── data
│   ├── dist
│   ├── .env
│   ├── .git
│   ├── .gitignore
│   ├── index.js
│   ├── medusa-config.js
│   ├── medusa-db.sql
│   ├── node_modules
│   ├── package.json
│   ├── package-lock.json
│   ├── README.md
│   ├── src
│   ├── tsconfig.json
│   ├── tsconfig.spec.json
│   └── uploads
```
Open up `medusa-config.js` in the root of your app folder.

In the projectConfig section, comment out the line `database_database: "./medusa-db.sql",` and add `database_url: DATABASE_URL`.

```js
/** @type {import('@medusajs/medusa').ConfigModule["projectConfig"]} */
const projectConfig = {
  jwtSecret: process.env.JWT_SECRET,
  cookieSecret: process.env.COOKIE_SECRET,
  //database_database: "./medusa-db.sql",
  database_url: DATABASE_URL,
  database_type: DATABASE_TYPE,
  store_cors: STORE_CORS,
  admin_cors: ADMIN_CORS,
  // Uncomment the following lines to enable REDIS
  // redis_url: REDIS_URL
}
```

Open up `package.json` in the root of your app folder and change the `start` script as follows:

```json
  "scripts": {
    "clean": "cross-env ./node_modules/.bin/rimraf dist",
    "build": "cross-env npm run clean && tsc -p tsconfig.json",
    "watch": "cross-env tsc --watch",
    "test": "cross-env jest",
    "seed": "cross-env medusa seed -f ./data/seed.json",
    "start": "medusa migrations run && medusa start",
    "start:custom": "cross-env npm run build && node --preserve-symlinks index.js",
    "dev": "cross-env npm run build && medusa develop",
    "build:admin": "cross-env medusa-admin build"
  },
```

Commit and push the changes you made to GitHub.

```bash
git commit -a -m "Updated medusa.config.js & package.json" 
git push
```

## Run Medusa Server on Oracle Cloud VM

In a new terminal session, ssh into your Oracle Cloud instance and clone your Medusa server app repo from GitHub.

```bash
git clone https://github.com/<username>/<reponame>.git
```

Change to your new directory holding the Medusa server app and install the Node dependencies:


```bash
cd my-medusa-store
npm install
```

Create a `.env` file in the root of `my-medusa-store` and add the following variables:

```
PORT=9000
JWT_SECRET=something
COOKIE_SECRET=something
DATABASE_TYPE=postgres
DATABASE_URL=postgres://medusa_admin:medusa_admin_password@localhost:5432/medusa_db
```

> *Use other values for `JWT_SECRET` and `COOKIE_SECRET` besides `something` for better security.*

To seed your database run the following command:

```bash
npm run seed
```

## Testing the Medusa app server

### Give public access to the server

In this step, you will add an ingress rule to the `medusa` subnet to allow internet connections on port `9000`.

Make sure you are still logged in via ssh to your Oracle instance.

Open the navigation menu, select **Networking**, and click on **Virtual Cloud Networks**.

![Select Virtual Cloud Networks](https://res.cloudinary.com/craigsims808/image/upload/v1684746915/articles/oracle-medusa/select-vcn-nav-menu_josftr.png)

Select the `medusa` VCN.

![Select medusa vcn](https://res.cloudinary.com/craigsims808/image/upload/v1684746915/articles/oracle-medusa/select-medusa-vcn_ckuuhy.png)

Select **Security List** link and click on the **Default Security List for medusa** link.

![Select Default Security List](https://res.cloudinary.com/craigsims808/image/upload/v1684746914/articles/oracle-medusa/select-default-security-list_hx75zv.png)

Select **Add Ingress Rules** to display the **Add Ingress Rules** modal. Fill in the ingress rule as follows:
- **Stateless**: `Checked`
- **Source Type**: `CIDR`
- **Source CIDR**: `0.0.0.0/0`
- **IP Protocol**: `TCP`
- **Source port range**:
- **Destination Port Range**: `9000`
- **Description**: `Allow HTTP connections`

![Add Ingress Rules](https://res.cloudinary.com/craigsims808/image/upload/v1684746914/articles/oracle-medusa/select-add-ingress-rules_xtsjij.png)

Click **Add Ingress Rules**. Now your server is accessible via HTTP.

![List of Ingress Rules](https://res.cloudinary.com/craigsims808/image/upload/v1684746913/articles/oracle-medusa/list-of-ingress-rules_wn0jsq.png)

### Configure Ubuntu Firewall

Follow these steps to configure the firewall settings for your Medusa server. In a new terminal session, ssh into your medusa server. 

Run the following commands to update the `iptables` configuration to allow HTTP traffic.

```bash
sudo iptables -I INPUT 6 -m state --state NEW -p tcp --dport 9000 -j ACCEPT
sudo netfilter-persistent save
```

### Start your Medusa app server

Ensure you are still logged in via ssh to your Oracle instance, and run the following command to start your Medusa app server.

```bash
npm run start
```

Now you can access it over the internet on `http://x.x.x.x:9000`, where `x.x.x.x` is your server's IP address.

If you visit the endpoint, `http://x.x.x.x:9000/health` you should see an `OK` message. This confirms that your server is working.

![health check](https://res.cloudinary.com/craigsims808/image/upload/v1684755319/articles/oracle-medusa/health-check_wwfpfq.png)

Visit the endpoint `http://x.x.x.x:9000/store/products`, and you should see a list of products in your database.

![products check](https://res.cloudinary.com/craigsims808/image/upload/v1684755320/articles/oracle-medusa/products-check_ctkes1.png)

With that, your Medusa server should be working as expected.

## Conclusion

This tutorial showcased minimal deployment of a Medusa server on Oracle Cloud. Here are some additional configurations you can make:

- [Install the MinIO plugin](https://docs.medusajs.com/plugins/file-service/minio) to handle image uploads to your Medusa backend.
- [Install](https://redis.io/docs/getting-started/installation/) and [configure Redis](https://docs.medusajs.com/development/backend/configurations#redis) if you want scheduled jobs.
- [Install PM2](https://pm2.io/docs/runtime/guide/installation/) to manage and ensure availability of your Medusa server.
- [Install Nginx](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/) to configure a domain name.
- Add an SSL certificate for your domain name.
- Set up a CI/CD pipeline between your development environment and your deployment.
- Secure your server.

Oracle Cloud Free Tier provides a convenient way for developers to test and deploy Medusa ecommerce apps. I hope this tutorial has provided sufficient knowledge on how you could host a Medusa server app on Oracle Cloud.

Enjoyed my tutorial? Help me keep creating free content by donating today. Every contribution makes a big difference! Thank you.

[![Buy Me A Coffee](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1smrl1f7ygsgbqxyypa.png)](https://www.buymeacoffee.com/markmunyaka)

If there are any issues or questions, feel free to comment with your query.