Among the plethora of available hosts to us, AWS EC2 is a strong contender.
Before I start any religious war on hosting, deployment, and DevOps, I’ll throw this caveat early on: There’s more than one way to cook an egg. (There’s a version that involves a cat that some pet-lovers might offensive). There are a hundred different alternatives to AWS, as there are a thousand different ways do deploy Shopify inside AWS.
I’ll describe the one that works for me, simply because I have a specific set of requirements. If you believe your setup works as well, please comment it down below.
We then immediately disqualify AWS t2.micro and Heroku’s free tier. In my experience, t2.micro's sshd dies on me often, that it requires me to terminate the instance (yes terminate, not stop), and redo everything all over again. Heroku’s free tier tends to sleep when not needed, and you will never want that for your customers.
The last thing I want to do is tear my hair debugging erroneous Chef scripts or Kubernetes pods going crazy. I do not wish to add another level of complexity.
I am experimenting on several Shopify apps at a time, and I will not stop anytime soon. I do not want my bill to behave similarly. Linear cost growth, where there is an increase in price for every app added, is not enough to be considered cost-effective.
I require SSH and sudo access to all my instances. Call me a control freak.
With all the disclaimers out of the way, we can begin!
Go ahead and create an m3.medium Ubuntu EC2 instance for our server. This instance type seems to be the minimum for a production-grade server. You can cram up to 5 low-traffic Shopify apps in this type. Your mileage will vary widely, depending on the traffic and amount of computation happening for each Shopify app.
Each one of your apps will need a URL. It is, therefore, essential to have the domains or subdomains pointed to our server early on. Several configurations will require such URL, and setting them up now will prevent any future hassle.
I personally use AWS Route53 to configure my DNS, but any DNS setup would do, as long as the URL is pointed correctly to the correct server. For our example, we set our demo app to app1.klaudsol.com.
Your instance may have more than one hostname associated with it. We will be using Nginx to allow virtual hosting and enable multiple apps with multiple hostnames to live on our server in harmony. We will see more of Nginx virtual hosting later.
Once you have your newly-minted server, install docker and docker-compose. Make sure you executed the “Executing the Docker Command Without Sudo” section.
To test if your docker setup is flawless, run the following command:
docker run hello-world
If done right, you should see the following response:
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
We plan on deploying multiple apps in this server to squeeze it with as much value as we can run away with. We organize these apps by placing them in a single subdirectory.
For the purpose of example, we clone all of our apps and place them inside the directory /home/ubuntu/code
. In the meantime, we only have one app to deploy, but we know where to add succeeding apps.
cd /home/ubuntu/code
git clone git@github.com:klaudsol/shopify-app-under-20-minutes.git app1
Aside from a common source code base, your production app must be totally separate and independent of your development app, for various good reasons. Development apps are expected to evolve and change quickly as new features and fixes are written. Production apps are expected to be stable, reliable, and always available for the customer.
Since we are registering a totally new app on the Shopify Partner dashboard, some of our environment variables will need updating. Check that these variables contain the proper values:
6.1) SHOPIFY_API_KEY
- double-check that this matches with the Production app API key, and not the one from development.
6.2) SHOPIFY_API_SECRET
- Like the API key, we mustn’t forget to point this to the new production app value.
6.3) HOST
- Ensure that this value matches the URL that we have set in step 2.
You need to add these files to configure how we run our docker containers.
/home/ubuntu/code/docker-compose.yml
We see from the code above that we use the jwilder/nginx-proxy
image (line 3
) to set up a reverse proxy. This reverse proxy enables us to host multiple apps simultaneously while being accessible from a single port (443).
Using an nginx image as a reverse proxy also allows us to centralize our certificates for easier management, as we shall see in step 7.
Lines 11 - 21
defines the container needed for us to run app1. Ensure that the VIRTUAL_HOST
environment matches the URL declared in step 2 for the proxy to work.
To add more apps, just add another entry in the docker-compose.yml
(e.g. app2
), with the appropriate VIRTUAL_HOST
and matching DNS declaration.
Line 15
tells us to execute /app/entrypoint.sh
on our script. You may have noticed already that we do not have this file yet in our system, and it is our task to create it now.