Deployment of a Swift Vapor App to the AWS EC2 Cloud

At SwiftyBeaver we are all living, breathing, and even dreaming Swift. So it was no surprise that we chose server-side Swift to power the backend of our website as soon as it became available.


So far there are just basic tutorials about getting-started with server-side Swift deployment to Heroku or DigitalOcean but no real-life, production-ready instructions with Amazon Web Services as cloud provider which is maybe more suited for larger web apps in production.

That’s why I am explaining how we did the production-ready installation, setup & deployment of our Swift 3 web app using the following stack:

  • Web app “SwiftyBeaver Website” (source code on Github)
  • Vapor 1.18 as web framework
  • Swift 3.0.1 as programming language
  • Ubuntu 16.04 as operating system
  • AWS EC2 cloud instances
  • AWS ELB (Elastic Load Balancer) as load balancer & HTTPS proxy

It is recommended that you already have a basic knowledge of Swift & AWS. All used source code, including the web app project itself is open-source and can act as starting point for your own projects.

Our web app has helpful additional functionality which makes a typical Vapor web app even better for production. For example it logs errors to a file via SwiftyBeaver’s Vapor provider (yes, we are eating our own dog food, hehe). It also has contains useful middleware to deal with CORS, Cache-Control Headers and the automatic serving of Error Pages like 404. For more detail see its source code on Github).


What you will build

In the next paragraphs I will explain the following:

  1. Setup of an Ubuntu 16.04 EC2 instance for Swift 3.0.1 & Vapor
  2. Installation of the web app as a Linux system service allowing automatic restart on failure, on boot, etc.
  3. Deployment of a local web app to the EC2 instance
  4. Using Elastic Load Balancer to access the web app via HTTPS

1. AWS EC2 Instance Creation & Swift Setup

Instance Creation:

As first step, you need an instance (aka “virtual server” or just “server”). Please go to your Amazon Web Services EC2 Console and launch the EC2 instance “Ubuntu Server 16.04 LTS (HVM), SSD Volume Type”. 
There are many different Amazon Machine Images (AMIs), you can search for that particular AMI in the North Virginia region (US-East1) via its name "ami-40d28157".

For a quick test a t2.micro instance with 20 GB SSD is enough, especially if you put it behind a CDN like we actually do — but that is explained maybe in follow-on article.

Anyway, with your new instance please create a new security group which opens port 8080 to world and add your current IP for SSH. Later you need to change the port 8080 setting to just be accessible by the load balancer’s security group. But more on that further down below.

Initial Instance Setup:

When your new instance is up & running, connect to it via SSH (instructions for that you get in the EC2 console when you activate that instance and click on “Connect”).

Now you update the server, install auto-upgrades and a time service and finally initiate a reboot. Select “Yes” if a colored window appears:

After some seconds please re-connect to the instance via SSH as ubuntu user to install Swift & Vapor with the following lines:

You can now check if Swift was properly installed, where its binary is located and if it is generally compatible with Vapor:

And finally, you need to install Vapor Toolbox which is helping with building Vapor projects more efficiently with that single line of code:

You have now created an EC2 instance with Ubuntu 16.04, Swift 3 and Vapor properly installed. Maybe now is a good time to create a snapshot :)


2. Your App as a Linux System Service

Swift web apps behave like normal desktop apps. You can run them but they will stop if they crash, if you end the SSH connection and if your server reboots. And if your web app stopped it means your web app is not online anymore … which is not good.

Hello systemd!

As solution to make an app always running and restarting, Linux has a “thing” called a systemd service. Until recently Ubuntu used another “thing” called upstart but since last year systemd is used, but anyway, just in case someone asks and you want to shine.

With a systemd service you can do the following things to start, stop, restart and check the general status of your app (service).

Explaining how systemd is working in detail would also go far beyond this article and yes, it is a non-trivial business which goes deep into the abyss of Linux.

Like every good chef, I have already something prepared for you. In this case it is the file called app.service which you please download to your Mac. The service has the name app which should make it easier for you to keep in mind.

Important: after downloading please open that file in a text editor and replace “SBWebsite” which is the name of our web app with the name of your web app and save the file.

You can now upload that service file to your EC2 instance via scp (which is the little brother of SSH), replacing YOUR_SERVER_IP with your server IP or CNAME:

After uploading please log into your EC2 instance, move the service file to its correct folder and check if things are looking good:

If no errors are printed to the console then all is fine.


3. Deploy App to EC2 Instance

You have now a properly setup EC2 instance and a service which is waiting to be started. But there is still something missing on your server: your web app and its source code!

Deployment is another very complex topic and there are many, many different way to get source code onto a server.

In our case here I have again something prepared. It is the small shell script deploy.sh which you please download into your Vapor web app’s main folder.

After downloading the shell script, please open it and read it carefully to understand what it is doing before executing it! In a nutshell, the script is compressing the Git repository of the folder it is it is currently in, following by uploading and decompressing it on your server. Additionally it is updating the Swift packages of your project and restarts your app service via the systemctl restart app call.

Run the script with the following lines (line 1 is just needed at the first call). Again, please replace YOUR_SERVER_IP with your server IP or CNAME:

After some time of building the script will show the status of your restarted service. That’s it! You can now open your web browser and enter your YOUR_SERVER_IP:8080 to see your live web app 🎉


4. Elastic Load Balancer for SSL

Currently you still serve your web app on port 8080 which is unusual. Additionally it is not SSL-secured, meaning it does not run use the HTTPS port 443.

To serve the web app via HTTPS over port 443 you add an AWS EC2 
Elastic Load Balancer (ELB) in front of your instance which contains the SSL certificate, is doing all the SSL-work and finally routes / proxies all requests to port 443 to the instance(s) which serve the web app on port 8080.

The security group of the instance should now be changed to just allow connections from the load balancer (and maybe your office IP for SSH) so that everyrequest has to go through the load balancer.

You can also use the Elastic Load Balancer to serve “normal” HTTP traffic over port 80 and you can even route all traffic incoming on ports 80 and 443 to your instance’s port 8080. Amazon’s Elastic Load Balancer is a very powerful tool and I recommend to ALWAYS use one and be it just to offload SSL certificate processing.


Conclusion

The setup of a server-side Swift web app at a “serious” cloud provider like Amazon Web Services is more complex than let’s say Heroku for example.

But the trickiest part is really the initial instance setup and the creation of a service file to let your app run infinitely with which I could help a bit.

Everything else, like ELB is for the moment maybe just sugar but should show you how much more powerful Amazon Web Services is over Heroku and similar. Once you are on AWS you can really leverage other AWS products/features like Route 53, Amazon RDS, CodeDeploy, S3 Cloud Storage, VPC, CloudFront, etc. which just work better when used from within EC2.

And please don’t forget to do proper logging to really learn what is going on during development and release in your server-side Swift apps and your “normal” Swift apps with SwiftyBeaver 😊

Happy Logging & server-side Swift’ing!

Sebastian