Build a Ruby and Postgres app in Azure App Service on Linux

App Service on Linux provides a highly scalable, self-patching web hosting service using the Linux operating system. This tutorial shows how to create a Ruby app and connect it to a PostgreSQL database. When you're finished, you'll have a Ruby on Rails app running on App Service on Linux.

Ruby on Rails app running in Azure App Service

In this tutorial, you learn how to:

  • Create a PostgreSQL database in Azure
  • Connect a Ruby on Rails app to PostgreSQL
  • Deploy the app to Azure
  • Update the data model and redeploy the app
  • Stream diagnostic logs from Azure
  • Manage the app in the Azure portal

If you don't have an Azure subscription, create a free account before you begin.

Prerequisites

To complete this tutorial:

Prepare local Postgres

In this step, you create a database in your local Postgres server for your use in this tutorial.

Connect to local Postgres server

Open the terminal window and run psql to connect to your local Postgres server.

sudo -u postgres psql

If your connection is successful, your Postgres database is running. If not, make sure that your local Postgres database is started by following the steps at Downloads - PostgreSQL Core Distribution.

Type \q to exit the Postgres client.

Create a Postgres user that can create databases by running the following command, using your signed-in Linux username.

sudo -u postgres createuser -d <signed-in-user>

Create a Ruby on Rails app locally

In this step, you get a Ruby on Rails sample application, configure its database connection, and run it locally.

Clone the sample

In the terminal window, cd to a working directory.

Run the following command to clone the sample repository.

git clone https://github.com/Azure-Samples/rubyrails-tasks.git

cd to your cloned directory. Install the required packages.

cd rubyrails-tasks
bundle install --path vendor/bundle

Run the sample locally

Run the Rails migrations to create the tables the application needs. To see which tables are created in the migrations, look in the db/migrate directory in the Git repository.

rake db:create
rake db:migrate

Run the application.

rails server

Navigate to http://localhost:3000 in a browser. Add a few tasks in the page.

Ruby on Rails connects successfully to Postgres

To stop the Rails server, type Ctrl + C in the terminal.

Open Azure Cloud Shell

Azure Cloud Shell is a free, interactive shell that you can use to run the steps in this article. Common Azure tools are preinstalled and configured in Cloud Shell for you to use with your account. Select Copy to copy the code, paste it in Cloud Shell, and then press Enter to run it. There are a few ways to open Cloud Shell:

Select Try It in the upper-right corner of a code block. Example of Try It for Azure Cloud Shell
Open Cloud Shell in your browser. Launch Azure Cloud Shell button
Select the Cloud Shell button on the menu in the upper-right corner of the Azure portal. Cloud Shell button in the Azure portal

Create Postgres in Azure

In this step, you create a Postgres database in Azure Database for PostgreSQL. Later, you configure the Ruby on Rails application to connect to this database.

Create a resource group

A resource group is a logical container into which Azure resources like web apps, databases, and storage accounts are deployed and managed. For example, you can choose to delete the entire resource group in one simple step later.

In the Cloud Shell, create a resource group with the az group create command. The following example creates a resource group named myResourceGroup in the West Europe location. To see all supported locations for App Service on Linux in Basic tier, run the az appservice list-locations --sku B1 --linux-workers-enabled command.

az group create --name myResourceGroup --location "West Europe"

You generally create your resource group and the resources in a region near you.

When the command finishes, a JSON output shows you the resource group properties.

Create a Postgres server

Create a PostgreSQL server with the az postgres server create command.

Run the following command in the Cloud Shell, and substitute a unique server name for the <postgres-server-name> placeholder. The server name needs to be unique across all servers in Azure.

az postgres server create --location "West Europe" --resource-group myResourceGroup --name <postgres-server-name> --admin-user adminuser --admin-password My5up3r$tr0ngPa$w0rd! --sku-name GP_Gen4_2

When the Azure Database for PostgreSQL server is created, the Azure CLI shows information similar to the following example:

{
  "administratorLogin": "adminuser",
  "earliestRestoreDate": "2018-06-15T12:38:25.280000+00:00",
  "fullyQualifiedDomainName": "<postgres-server-name>.postgres.database.azure.com",
  "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.DBforPostgreSQL/servers/<postgres-server-name>",
  "location": "westeurope",
  "name": "<postgres-server-name>",
  "resourceGroup": "myResourceGroup",
  "sku": {
    "capacity": 2,
    "family": "Gen4",
    "name": "GP_Gen4_2",
    "size": null,
    "tier": "GeneralPurpose"
  },
  < Output has been truncated for readability >
}

Configure server firewall

In the Cloud Shell, create a firewall rule for your Postgres server to allow client connections by using the az postgres server firewall-rule create command. When both starting IP and end IP are set to 0.0.0.0, the firewall is only opened for other Azure resources. Substitute a unique server name for the <postgres-server-name> placeholder.

az postgres server firewall-rule create --resource-group myResourceGroup --server <postgres-server-name> --name AllowAllIps --start-ip-address 0.0.0.0 --end-ip-address 255.255.255.255

Tip

You can be even more restrictive in your firewall rule by using only the outbound IP addresses your app uses.

Connect to production Postgres server locally

In the Cloud Shell, connect to the Postgres server in Azure. Use the value you specified previously for the <postgres-server-name> placeholders.

psql -U adminuser@<postgres-server-name> -h <postgres-server-name>.postgres.database.azure.com postgres

When prompted for a password, use My5up3r$tr0ngPa$w0rd!, which you specified when you created the database server.

Create a production database

At the postgres prompt, create a database.

CREATE DATABASE sampledb;

Create a user with permissions

Create a database user called railsappuser and give it all privileges in the sampledb database.

CREATE USER railsappuser WITH PASSWORD 'MyPostgresAzure2017';
GRANT ALL PRIVILEGES ON DATABASE sampledb TO railsappuser;

Exit the server connection by typing \q.

Connect app to Azure Postgres

In this step, you connect the Ruby on Rails application to the Postgres database you created in Azure Database for PostgreSQL.

Configure the database connection

In the repository, open config/database.yml. At the bottom of the file, replace the production variables with the following code.

production:
  <<: *default
  host: <%= ENV['DB_HOST'] %>
  database: <%= ENV['DB_DATABASE'] %>
  username: <%= ENV['DB_USERNAME'] %>
  password: <%= ENV['DB_PASSWORD'] %>

Save the changes.

Test the application locally

Back in the local terminal, set the following environment variables:

export DB_HOST=<postgres-server-name>.postgres.database.azure.com
export DB_DATABASE=sampledb 
export DB_USERNAME=railsappuser@<postgres-server-name>
export DB_PASSWORD=MyPostgresAzure2017

Run Rails database migrations with the production values you just configured to create the tables in your Postgres database in Azure Database for PostgreSQL.

rake db:migrate RAILS_ENV=production

When running in the production environment, the Rails application needs precompiled assets. Generate the required assets with the following command:

rake assets:precompile

The Rails production environment also uses secrets to manage security. Generate a secret key.

rails secret

Save the secret key to the respective variables used by the Rails production environment. For convenience, you use the same key for both variables.

export RAILS_MASTER_KEY=<output-of-rails-secret>
export SECRET_KEY_BASE=<output-of-rails-secret>

Enable the Rails production environment to serve JavaScript and CSS files.

export RAILS_SERVE_STATIC_FILES=true

Run the sample application in the production environment.

rails server -e production

Navigate to http://localhost:3000. If the page loads without errors, the Ruby on Rails application is connecting to the Postgres database in Azure.

Add a few tasks in the page.

Ruby on Rails connects successfully to Azure Database for PostgreSQL

To stop the Rails server, type Ctrl + C in the terminal.

Commit your changes

Run the following Git commands to commit your changes:

git add .
git commit -m "database.yml updates"

Your app is ready to be deployed.

Deploy to Azure

In this step, you deploy the Postgres-connected Rails application to Azure App Service.

Configure a deployment user

In the Azure Cloud Shell, configure deployment credentials with the az webapp deployment user set command. This deployment user is required for FTP and local Git deployment to a web app. The username and password are account level. They're different from your Azure subscription credentials.

In the following example, replace <username> and <password>, including the brackets, with a new username and password. The username must be unique within Azure. The password must be at least eight characters long, with two of the following three elements: letters, numbers, and symbols.

az webapp deployment user set --user-name <username> --password <password>

You get a JSON output with the password shown as null. If you get a 'Conflict'. Details: 409 error, change the username. If you get a 'Bad Request'. Details: 400 error, use a stronger password. The deployment username must not contain ‘@’ symbol for local Git pushes.

You configure this deployment user only once. You can use it for all your Azure deployments.

Note

Record the username and password. You need them to deploy the web app later.

Create an App Service plan

In the Cloud Shell, create an App Service plan in the resource group with the az appservice plan create command.

The following example creates an App Service plan named myAppServicePlan in the Basic pricing tier (--sku B1) and in a Linux container (--is-linux).

az appservice plan create --name myAppServicePlan --resource-group myResourceGroup --sku B1 --is-linux

When the App Service plan has been created, the Azure CLI shows information similar to the following example:

{ 
  "adminSiteName": null,
  "appServicePlanName": "myAppServicePlan",
  "geoRegion": "West Europe",
  "hostingEnvironmentProfile": null,
  "id": "/subscriptions/0000-0000/resourceGroups/myResourceGroup/providers/Microsoft.Web/serverfarms/myAppServicePlan",
  "kind": "linux",
  "location": "West Europe",
  "maximumNumberOfWorkers": 1,
  "name": "myAppServicePlan",
  < JSON data removed for brevity. >
  "targetWorkerSizeId": 0,
  "type": "Microsoft.Web/serverfarms",
  "workerTierName": null
} 

Create a web app

Create a web app in the myAppServicePlan App Service plan.

In the Cloud Shell, you can use the az webapp create command. In the following example, replace <app-name> with a globally unique app name (valid characters are a-z, 0-9, and -). The runtime is set to RUBY|2.3. To see all supported runtimes, run az webapp list-runtimes --linux.

# Bash
az webapp create --resource-group myResourceGroup --plan myAppServicePlan --name <app-name> --runtime "RUBY|2.3" --deployment-local-git
# PowerShell
az --% webapp create --resource-group myResourceGroup --plan myAppServicePlan --name <app-name> --runtime "RUBY|2.3" --deployment-local-git

When the web app has been created, the Azure CLI shows output similar to the following example:

Local git is configured with url of 'https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git'
{
  "availabilityState": "Normal",
  "clientAffinityEnabled": true,
  "clientCertEnabled": false,
  "cloningInfo": null,
  "containerSize": 0,
  "dailyMemoryTimeQuota": 0,
  "defaultHostName": "<app-name>.azurewebsites.net",
  "deploymentLocalGitUrl": "https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git",
  "enabled": true,
  < JSON data removed for brevity. >
}

You’ve created an empty new web app, with git deployment enabled.

Note

The URL of the Git remote is shown in the deploymentLocalGitUrl property, with the format https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git. Save this URL as you need it later.

Configure database settings

In App Service, you set environment variables as app settings by using the az webapp config appsettings set command in the Cloud Shell.

The following Cloud Shell command configures the app settings DB_HOST, DB_DATABASE, DB_USERNAME, and DB_PASSWORD. Replace the placeholders <appname> and <postgres-server-name>.

az webapp config appsettings set --name <app-name> --resource-group myResourceGroup --settings DB_HOST="<postgres-server-name>.postgres.database.azure.com" DB_DATABASE="sampledb" DB_USERNAME="railsappuser@<postgres-server-name>" DB_PASSWORD="MyPostgresAzure2017"

Configure Rails environment variables

In the local terminal, generate a new secret for the Rails production environment in Azure.

rails secret

Configure the variables required by Rails production environment.

In the following Cloud Shell command, replace the two <output-of-rails-secret> placeholders with the new secret key you generated in the local terminal.

az webapp config appsettings set --name <app-name> --resource-group myResourceGroup --settings RAILS_MASTER_KEY="<output-of-rails-secret>" SECRET_KEY_BASE="<output-of-rails-secret>" RAILS_SERVE_STATIC_FILES="true" ASSETS_PRECOMPILE="true"

ASSETS_PRECOMPILE="true" tells the default Ruby container to precompile assets at each Git deployment. For more information, see Precompile assets and Serve static assets.

Push to Azure from Git

In the local terminal, add an Azure remote to your local Git repository.

git remote add azure <paste-copied-url-here>

Push to the Azure remote to deploy the Ruby on Rails application. You are prompted for the password you supplied earlier as part of the creation of the deployment user.

git push azure master

During deployment, Azure App Service communicates its progress with Git.

Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 291 bytes | 0 bytes/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Updating branch 'master'.
remote: Updating submodules.
remote: Preparing deployment for commit id 'a5e076db9c'.
remote: Running custom deployment command...
remote: Running deployment command...
...
< Output has been truncated for readability >

Browse to the Azure app

Browse to http://<app-name>.azurewebsites.net and add a few tasks to the list.

Ruby on Rails app running in Azure App Service

Congratulations, you're running a data-driven Ruby on Rails app in Azure App Service.

Update model locally and redeploy

In this step, you make a simple change to the task data model and the webapp, and then publish the update to Azure.

For the tasks scenario, you modify the application so that you can mark a task as complete.

Add a column

In the terminal, navigate to the root of the Git repository.

Generate a new migration that adds a boolean column called Done to the Tasks table:

rails generate migration AddDoneToTasks Done:boolean

This command generates a new migration file in the db/migrate directory.

In the terminal, run Rails database migrations to make the change in the local database.

rake db:migrate

Update application logic

Open the app/controllers/tasks_controller.rb file. At the end of the file, find the following line:

params.require(:task).permit(:Description)

Modify this line to include the new Done parameter.

params.require(:task).permit(:Description, :Done)

Update the views

Open the app/views/tasks/_form.html.erb file, which is the Edit form.

Find the line <%=f.error_span(:Description) %> and insert the following code directly below it:

<%= f.label :Done, :class => 'control-label col-lg-2' %>
<div class="col-lg-10">
  <%= f.check_box :Done, :class => 'form-control' %>
</div>

Open the app/views/tasks/show.html.erb file, which is the single-record View page.

Find the line <dd><%= @task.Description %></dd> and insert the following code directly below it:

  <dt><strong><%= model_class.human_attribute_name(:Done) %>:</strong></dt>
  <dd><%= check_box "task", "Done", {:checked => @task.Done, :disabled => true}%></dd>

Open the app/views/tasks/index.html.erb file, which is the Index page for all records.

Find the line <th><%= model_class.human_attribute_name(:Description) %></th> and insert the following code directly below it:

<th><%= model_class.human_attribute_name(:Done) %></th>

In the same file, find the line <td><%= task.Description %></td> and insert the following code directly below it:

<td><%= check_box "task", "Done", {:checked => task.Done, :disabled => true} %></td>

Test the changes locally

In the local terminal, run the Rails server.

rails server

To see the task status change, navigate to http://localhost:3000 and add or edit items.

Added check box to task

To stop the Rails server, type Ctrl + C in the terminal.

Publish changes to Azure

In the terminal, run Rails database migrations for the production environment to make the change in the Azure database.

rake db:migrate RAILS_ENV=production

Commit all the changes in Git, and then push the code changes to Azure.

git add .
git commit -m "added complete checkbox"
git push azure master

Once the git push is complete, navigate to the Azure app and test the new functionality.

Model and database changes published to Azure

If you added any tasks, they are retained in the database. Updates to the data schema leave existing data intact.

Stream diagnostic logs

You can access the console logs generated from inside the container. First, turn on container logging by running the following command in the Cloud Shell:

az webapp log config --name <app-name> --resource-group myResourceGroup --docker-container-logging filesystem

Once container logging is turned on, run the following command to see the log stream:

az webapp log tail --name <app-name> --resource-group myResourceGroup

If you don't see console logs immediately, check again in 30 seconds.

Note

You can also inspect the log files from the browser at https://<app-name>.scm.azurewebsites.net/api/logs/docker.

To stop log streaming at any time, type Ctrl+C.

Manage the Azure app

Go to the Azure portal to manage the app you created.

From the left menu, click App Services, and then click the name of your Azure app.

Portal navigation to Azure app

You see your app's Overview page. Here, you can perform basic management tasks like stop, start, restart, browse, and delete.

The left menu provides pages for configuring your app.

App Service page in Azure portal

Clean up resources

In the preceding steps, you created Azure resources in a resource group. If you don't expect to need these resources in the future, delete the resource group by running the following command in the Cloud Shell:

az group delete --name myResourceGroup

This command may take a minute to run.

Next steps

In this tutorial, you learned how to:

  • Create a Postgres database in Azure
  • Connect a Ruby on Rails app to Postgres
  • Deploy the app to Azure
  • Update the data model and redeploy the app
  • Stream diagnostic logs from Azure
  • Manage the app in the Azure portal

Advance to the next tutorial to learn how to map a custom DNS name to your app.

Or, check out other resources: