Deploying a production-ready Medusa stack with a proper search engine and a cloud storage can be quite challenging, especially for beginners. However, with a few simple steps, you can deploy your own stack and have it up and running in no time. In this blog post, I will guide you through the process and provide you with some tips for troubleshooting.
Table of contents
- Components of the stack
- Requirements
- Setup your hosting and (sub)domains
- Setup Medusa stack
- Troubleshooting
Components of the stack
- Medusa Starter Default
- Minio Cloud Storage
- MeiliSearch
- Stripe
- ✨ SSL certificate via CloudFlare + LetsEncrypt
Requirements
- A VPS with at least 1GB of RAM.
- Docker and Docker Compose.
- A custom domain pointing to CloudFlare.
- A CloudFlare account.
Setup your hosting and (sub)domains
After getting access to your VPS you need to install and configure Docker and Docker compose.
Once you configure your server we are going to setup our domain. We will use CloudFlare to manage our domain's DNS records and handle the validation of a free SSL LetsEncrypt certificate in a easy way.
- Follow the instructions to add your site to your CloudFlare's account.
- Setup each A record to point your VPS IP address.
- Set your SSL/TLS encryption mode to "Full (strict)".
- Obtain your CloudFlare's API tokens.
Write down those codes, we are going to use them in following steps.
NOTE: Free TLDs like .ga, .ml or .tk won't work with the automatic DNS validation.
Setup Medusa stack
Once you installed Docker and secure your VPS, is time to set up the Medusa stack:
Step 1: Clone the Github repository.
You can clone the Github repository using the following command:
git clone https://github.com/beakman/medusa-stack-dockerized.git
Step 2: Edit the .env
file.
In the root directory of the cloned repository, you will find a file named .env.example
. You should copy this file and rename it to .env
. Open the .env
file and edit the values to suit your environment.
CloudFlare configuration
# LetsEncrypt CloudFlare verification
EMAIL=user@example.com
CERT_RESOLVER=letsencrypt
CLOUDFLARE_EMAIL=
CLOUDFLARE_API_KEY=
CLOUDFLARE_DNS_API_TOKEN=
Here you should provide the previously generated API token and your CloudFlare's email.
Medusa server
MEDUSA_DOMAIN=example.com
MEDUSA_CERT_RESOLVER=letsencrypt
ADMIN_CORS=https://admin.example.com
STORE_CORS=https://store.example.com
JWT_SECRET=
COOKIE_SECRET=
The admin and the store urls are not available yet. We are going to deploy them in another platform as it would be explained in another post.
The MEDUSA_DOMAIN
will be the address of our backend. We could use something like api.example.com
.
Traefik and dashboard
TRAEFIK_DOMAIN=traefik.example.com
TRAEFIK_CERT_RESOLVER=letsencrypt
TRAEFIK_USER=admin
TRAEFIK_PASSWORD_HASH= # echo $(htpasswd -nB user) | sed -e s/\\$/\\$\\$/g%
We will configure the rest of the variables in a second phase, since we will need to generate the api keys of some of the services.
Step 3: Start the containers.
The stack is composed of different docker-compose files to deploy each part of the stack. To start the containers, simply run:
sh start.sh
or more explicitly:
docker compose \
-f docker-compose.medusa.yml \
-f docker-compose.minio.yml \
-f docker-compose.search.yml \
-f docker-compose.traefik.yml \
up -d
Step 4: Access the Medusa stack.
If everything goes well, you could access the different components of your stack:
- Traefik dashboard: https://traefik.example.com
- Medusa server: https://api.example.com
- MeiliSearch: https://search.example.com
- Minio cloud storage: https://minio-console.example.com
You can check also the health of the containers by running:
turbo@test-medusa:~$ cd medusa-stack-dockerized/
turbo@test-medusa:~/medusa-stack-dockerized$ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
medusa-server-default "./develop.sh" backend running (healthy) 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp
medusa-stack-dockerized-meilisearch-1 "tini -- /bin/sh -c …" meilisearch running 0.0.0.0:7700->7700/tcp, :::7700->7700/tcp
medusa-stack-dockerized-postgres-1 "docker-entrypoint.s…" postgres running (healthy)
medusa-stack-dockerized-redis-1 "docker-entrypoint.s…" redis running
service-storage "/usr/bin/docker-ent…" storage running (healthy) 9000/tcp
traefik "/entrypoint.sh --pr…" traefik running 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, :::80->80/tcp, :::443->443/tcp
If any of the services is unhealthy you can debug it running:
docker-compose logs -f <service-name>
In the above command, replace <service-name> with the name of the service you want to debug.
Example:
turbo@test-medusa:~/medusa-stack-dockerized$ docker compose logs traefik
traefik | time="2023-03-02T22:47:26Z" level=info msg="Configuration loaded from flags."
traefik | time="2023-03-02T22:47:26Z" level=info msg="Traefik version 2.9.8 built on 2023-02-15T15:23:25Z"
traefik | time="2023-03-02T22:47:26Z" level=info msg="\nStats collection is disabled.\nHelp us improve Traefik by turning this feature on :)\nMore details on: https://doc.traefik.io/traefik/contributing/data-collection/\n"
traefik | time="2023-03-02T22:47:26Z" level=info msg="Starting provider aggregator aggregator.ProviderAggregator"
traefik | time="2023-03-02T22:47:26Z" level=info msg="Starting provider *traefik.Provider"
traefik | time="2023-03-02T22:47:26Z" level=info msg="Starting provider *docker.Provider"
traefik | time="2023-03-02T22:47:26Z" level=info msg="Starting provider *acme.ChallengeTLSALPN"
traefik | time="2023-03-02T22:47:26Z" level=info msg="Starting provider *acme.Provider"
traefik | time="2023-03-02T22:47:26Z" level=info msg="Testing certificate renew..." providerName=letsencrypt.acme ACME CA="https://acme-v02.api.letsencrypt.org/directory"
traefik | time="2023-03-03T22:47:26Z" level=info msg="Testing certificate renew..." providerName=letsencrypt.acme ACME CA="https://acme-v02.api.letsencrypt.org/directory"
traefik | time="2023-03-04T12:20:26Z" level=error msg="Error while Peeking first byte: read tcp 172.18.0.4:80->151.235.195.76:33779: read: connection timed out"
traefik | time="2023-03-04T22:47:26Z" level=info msg="Testing certificate renew..." providerName=letsencrypt.acme ACME CA="https://acme-v02.api.letsencrypt.org/directory"
Configure Minio cloud storage
- Access your Minio console and create a new bucket.
- Set the policy of the bucket to "Public".
- Go to "Access keys" and generate the access key and the secret key.
- Set the variables in
.env
accordingly.
MINIO_ENDPOINT=https://minio.example.com
MINIO_BUCKET=medusa-test
MINIO_ACCESS_KEY=supers3cr3t
MINIO_SECRET_KEY=v3rystr0ngpassw0rd
MINIO_ROOT_USER=admin
MINIO_ROOT_PASS=changeme1234
MINIO_DOMAIN=minio.example.com
MINIO_CONSOLE_DOMAIN=minio-console.example.com
MINIO_CERT_RESOLVER=letsencrypt
Configure MeiliSearch variables
MEILI_DOMAIN=search.example.com
MEILI_CERT_RESOLVER=letsencrypt
MEILISEARCH_HOST=https://search.example.com
MEILISEARCH_API_KEY=
MEILI_MASTER_KEY=
Secure your MeiliSearch instance.
Once you set the master key and restart the container your request will be protected. Check the endpoint https://search.example.com/keys, it should show a message informing you the request is not authorized.
Troubleshooting
P: Docker containers don't finish getting up
S: Check that your VPS has at least 1GB of RAM available.
P: Errors requesting the SSL LetsEncrypt certificates.
S: Don't use free TLDs like .ga, .ml, .tk, etc. Ensure you wait enough time to propagate the DNS changes.
P: Can't login to Traefik dashboard.
S: Ensure you generate a correct user and hashed password.
I hope you found my post useful! If you did, please consider giving it a star on Github. It will help others find and benefit from the post as well.
Thank you for your support!
Top comments (0)