A Beginner’s Guide to App Deployment via PaaS Provider
Tags: tutorial, beginners, webdev, programming
Published: 2024-02-20 03:03:34

## Contents

- [Introduction](#introduction)
- [Basics](#basics)
- [Deployment method](#deployment-method)
  - [App preparation](#app-preparation)
  - [Deploying](#deploying)
- [Backing and restoring the production database](#backing-and-restoring-the-production-database)
- [Conclusion](#conclusion)
- [Resources](#resources)

## Introduction

You’ve spent the last month finishing up your very cool application and now you want to show it off to the world. You try to remember the lecture video on the topic a few months ago but fail to remember the details. Then you look online and come across a blog with the title: A Beginner’s Guide to App Deployment via Paas Provider. Sounds strangely close to exactly what you are looking for.

There are two types of cloud computing services, Platform as a Service (PaaS) and Infrastructure as a Service (IaaS). PaaS simplifies and abstracts away the infrastructure complexity, making it easier to deploy and manage applications. Because of this, it lets developers focus more on coding. On the other hand, IaaS offers more flexibility over the infrastructure components and allows for customization to meet specific requirements. This blog will go over the basics and step-by-step protocol on how to deploy a React-Rails-PostgreSQL app using a PaaS provider, [Render](https://render.com/). Other popular PaaS providers are [Heroku](https://www.heroku.com/), [Google App Engine](https://cloud.google.com/appengine), and [Microsoft Azure App Service](https://azure.microsoft.com/en-us/).

## Basics

When creating our app for production, we undergo several environments depending on the stage of development.

> The **development** environment is when working on the app locally and running code on your personal computer. We are able to use debugging tools like byebug and code analysis tools like ESLint.

> The **test** environment can be utilized when running test runners like RSpec or Jest. The goal is for the code to run as quickly as possible so we can get rapid feedback. Like in the development environment, we are able to use debugging tools when needed.

> When we are ready for deployment, we undergo the **staging** environment. This environment is a replica of the production environment and is used to perform final testing before deploying changes to the live app. We can catch any issues that may have been missed in the testing environment. 

> Finally we have the **production** environment. This is post-deployment where the app is accessed by end users. This live environment has to be stable, secure, and highly available.

The staging environment is used after deployment to test the app in a more production-like environment and to serve to end users. Because the steps for staging are generally similar to those of production, we will skip staging for simplicity.

Before diving into the method, we need to think about cost. Although there are free options for many PaaS providers, they are only suitable for smaller and less complex apps. Most free options limit the app in terms of usage so apps that require more resources would probably have to think about the paid plan. Some free plans may also spin-down an app that has been inactive for some time, meaning it will take a minute or so starting back up from this phase. Other things to think about are scalability and performance. This is asking yourself, “how large do I expect this app to be?” and “will this app be performing at the level I want it to for the traffic it will receive?”. Again, using the free plan will limit these things to a significant degree so apps that require more will have to upgrade to the paid plan.

## Deployment method

Assuming we have an app ready for production, the first thing we need to do (if not done already), is to link to a remote GitHub repo for the app. Normally, we should already have this during the development stage, but if not, here is a quick rundown:

1. Create a repo on GitHub
2. Connect local repo to remote repo with `git remote add origin <SSH>`
3. Add, commit, and push code to GitHub with `git push -u origin main` on the first commit

### App preparation

We need to update some of our configuration files. Let’s update database.yml to make sure that we properly set up our production database. If we are using Render, we can connect via `url: <%= ENV[‘DATABASE_URL’] %>` using the external database url. For now, we can just set up the url key and leave it alone until we have our database ready.

Next we need to update config/puma.rb and config/environments/production.rb.

```
# config/puma.rb

workers ENV.fetch("WEB_CONCURRENCY") { 4 }
preload_app!
```

```
# config/environments/production.rb

config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? || ENV['RENDER'].present?
```

Everytime we deploy the app, we want a script to run that helps build our frontend and backend code. We will create this script file as bin/render-build.sh.

```
#!/usr/bin/env bash

echo 'Running render-build.sh...'
# exit on error
set -e

# verbose mode
set -x

# builds the front end code
rm -rf public
npm install --prefix client && npm run build --prefix client
cp -a client/build/. public/

# builds the back end code
bundle install
bundle exec rake db:migrate
bundle exec rake db:seed

echo '...finished running render-build.sh'
```

A couple things to note here. The first line `#!/usr/bin/env bash` is the shebang line that instructs how this script file should be read. We set the exit on error and verbose mode to help us check the progress of the script as it runs. To build our frontend, we remove the public folder, install dependencies, build our production version, and save that build in our empty public folder. Our backend consists of installing the gems, running the migrations, and seeding the database if it is our first deployment. After our first deployment, we would comment out the `bundle exec rake db:seed` to avoid duplicate records.

Once we have our script file ready, we want to give it permission to make sure it is executable. On the terminal, run:

```
$ chmod a+x bin/render-build.sh
```

Pressing enter on this won’t show any response. To check if the file executable, run:

```
$ ls -l bin
```

If there are three x’s in the permission string, we are good to go.

## Deploying

Go to render.com and create a PostgreSQL instance as our production database. Make sure that the PostgreSQL version is the same as the version used in development. Create the database and wait for it to be available. In the meanwhile, start a new tab and go back to render.com. This time we want to create a web service. After filling out the requested information, go down to where it says Build Command. Here we want to run our script from above:

```
./bin/render-build.sh
```

Our Start Command should run our puma.rb file, like so:

```
bundle exec puma -C config/puma.rb
```

Now we need to connect our web service to our local app and our newly created PostgreSQL production database. Remember, to set the Ruby version to that of the app.

To connect to our local app, set the key to `RAILS_MASTER_KEY` and the value to the secret file master.key in your app. If there is no master.key, we can create one using these steps.

Delete config/credentials.yml.enc
Run this code in the terminal to create a new master.key and credentials.yml

```
EDITOR="code --wait" bin/rails credentials:edit
```

To connect to our production database, set another key to `DATABASE_URL` and the value to the internal database url. On our first tab with the Render database, go to connections to find this url.

That’s it. You have now deployed your first web app along with its database.

## Backing and restoring the production database

If you end up using the free version, keep it in mind that on Render, these instances will only be available for a few months. When nearing the end of its life, we need to consider backing and restoring the databases if we want to keep our app alive. 

Backing up the database requires us to modify the PSQL connection string (PSQL Command) to run PSQL’s `pg_dump`. This creates a .sql file of the database. Below is a template of what this string should look like:

```
PGPASSWORD=your_password pg_dump -h your_host -U your_username --format=custom --no-acl --no-owner your_database > your_database_backup.sql
```

Replace
`your_password` with the actual PostgreSQL password shown on Render
`your_host` with the actual PostgreSQL host shown on Render
`your_username` with the actual PostgreSQL username shown on Render
`your_databse` with the actual PostgreSQL database name
`your_databse_backup.sql` with whatever name you want for the backup file

Copy and paste the PGPASSWORD into the terminal in a directory without a git repo, since we don't want this backup file to get pushed to GitHub.

After creating our backup file, we can now delete the expiring PostgreSQL instance and create a new one, along with the new production database. We can create the database using psql or pgAdmin. Finally, we need to modify the PSQL connection string once more, but this time to run PSQL’s `pg_restore`. Below is the template once more:

```
PGPASSWORD=your_password pg_restore -h your_host -U your_username --verbose --clean --no-acl --no-owner -d your_database your_database_backup.sql
```

Replace
`your_password` with the new PostgreSQL password shown on Render
`your_host` with the new PostgreSQL host shown on Render
`your_username` with the new PostgreSQL username shown on Render
`your_databse` with the new PostgreSQL database name
`your_databse_backup.sql` with whatever named your backup file in the previous step

## Conclusion

You now know how to deploy your app using a PaaS provider. To review, the steps are:

1. Connect to a repo GitHub
2. Update database.yml, puma.rb, and production.rb
3. Create our build script, render-build.sh and make it executable
4. Create the PostgreSQL server for our production database on Render
5. Create the web service on Render

Optional:

1. Create a backup file using a modified PSQL command to run pg_dump
2. Restore a database through the backup file using a modified PSQL command to run pg_restore

> This guide is meant to serve as a learning tool for people who are just starting to learn how to deploy apps. Please read the official documentation linked below for more information. Thank you for reading and if you have any questions or concerns about some of the material mentioned in this guide, please do not hesitate to contact me at jjpark987@gmail.com.

## Resources

- [Render Documentation](https://docs.render.com/)