Deploy Rails 7.2.1 + Managed Postgresql App to Azure Using Kamal 2.1.1
2024-11-08
Let's setup a Rails app that uses Postgresql as the DB and deploy it to Azure using Kamal. Note that the Postgresql in production environment will be using the managed Postgresql instance in Azure.
1. Settings up Rails App
1.1 Create a new Rails app
$ rails new rails-pg-kamal-deployed-to-azure --minimal --database=postgresql
1.2 Add a scaffold
$ cd rails-pg-kamal-deployed-to-azure
$ rails g scaffold post title:string content:text
1.3 Locally set up Postgresql for Testing
- Create
docker-compose.development.yml
version: '3.8'
services:
db:
image: postgres:16.2-alpine
container_name: rails-pg-kamal-deployed-to-azure-development-db
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=rails_pg_kamal_deployed_to_azure_development
ports:
- '5442:5432'
volumes:
- db:/var/lib/postgresql/data
volumes:
db:
driver: local
-
Note the username, password, db name, and the port above
-
config/database.yml
development:
<<: *default
database: rails_pg_kamal_deployed_to_azure_development
port: 5442
host: localhost
username: postgres
password: postgres
- Start PG DB locally
$ docker-compose -f docker-compose.development.yml up --build
1.4 Run migration
- You can omit running
$ rails db:create
because database will be created by starting docker-compose - Run
$ rails db:migrate
1.5 Add Routes
routes.rb
root "posts#index"
1.6 Start server locally
$ ./bin/dev
- Go to localhost:3000
2. Azure
2.1 Everything except Postgresql
Please follow the instructions here.
You may want to skip "2.2 Create Container Registry", if you already have a container registry to use.
2.2 Postgresql
2.2.1 Setting up Basics
- Go "Create a resource" page
- In the left hand side bar, select "Databases".
- Click "Create" for "Azure Database for PostgreSQL"
- Select the Subscription and Resource Group
- Under Server Details, provide info like below.
- Make sure to Click "Configure server" and size down the DB server appropriately.
- Provide info to setup "Authentication" for the db.
- Click "Review + create".
PG
- pgadminops
- 8132a28d96bc@
2.2.2 Networking
- Setup the Networking setup like below
- Click "Create".
2.2.3 Dealing with extension "plpgsql" is not allow-listed for "azure_pg_admin" users in Azure Database for PostgreSQL
error.
When you use Azure Database for PostgreSQL, you will see the following error.
ERROR 2024-10-16T06:56:56.367508062Z bin/rails aborted!
2024-10-16T06:56:56.367929864Z ActiveRecord::StatementInvalid: PG::FeatureNotSupported: ERROR: extension "plpgsql" is not allow-listed for "azure_pg_admin" users in Azure Database for PostgreSQL (ActiveRecord::StatementInvalid)
2024-10-16T06:56:56.367975864Z HINT: to learn how to allow an extension or see the list of allowed extensions, please refer to https://go.microsoft.com/fwlink/?linkid=2281561
2024-10-16T06:56:56.368497566Z /rails/db/schema.rb:15:in `block in <top (required)>'
2024-10-16T06:56:56.368509766Z /rails/db/schema.rb:13:in `<top (required)>'
2024-10-16T06:56:56.368641767Z
2024-10-16T06:56:56.368669567Z Caused by:
2024-10-16T06:56:56.368799368Z PG::FeatureNotSupported: ERROR: extension "plpgsql" is not allow-listed for "azure_pg_admin" users in Azure Database for PostgreSQL (PG::FeatureNotSupported)
2024-10-16T06:56:56.368809368Z HINT: to learn how to allow an extension or see the list of allowed extensions, please refer to https://go.microsoft.com/fwlink/?linkid=2281561
2024-10-16T06:56:56.368998368Z /rails/db/schema.rb:15:in `block in <top (required)>'
2024-10-16T06:56:56.369025368Z /rails/db/schema.rb:13:in `<top (required)>'
2024-10-16T06:56:56.369153969Z Tasks: TOP => db:prepare
2024-10-16T06:56:56.369269569Z (See full trace by running task with --trace)
2024-10-16T06:57:00.413100892Z bin/rails aborted!
- Go to
Azure Database for PostgreSQL
- Select the PG database
- Click Settings > Server Parameters
- Search for
azure.extensions
. - Click
Value
column - Check
PLPGSQL
.
See link for more info.
3. Setting up Kamal 2 for Rails
3.1. Follow these steps here
Follow Step 3 in the following link, but you can skip Step 3.1.
3.2. Update .kamal/secrets
- Add secrets in
.kamal/secrets
.
RAILS_MASTER_KEY=$(cat config/master.key)
KAMAL_REGISTRY_PASSWORD=...
POSTGRES_PASSWORD=...
3.3. Update config/database.yml
production:
<<: *default
database: rails_pg_kamal_deployed_to_azure_production
username: pgadminops
password: <%= ENV["POSTGRES_PASSWORD"] %>
host: <%= ENV["DB_HOST"] %>
3.4. Update config/deploy.yml
- Comment out
proxy.host
.
Example:
<% require "dotenv"; Dotenv.load(".env") %>
# Name of your application. Used to uniquely configure containers.
service: rails-pg-kamal-deployed-to-azure
# Name of the container image.
image: serv/rails-pg-kamal-deployed-to-azure
# Deploy to these servers.
servers:
web:
hosts:
- x.y.z.a
# job:
# hosts:
# - 192.168.0.1
# cmd: bin/jobs
# Enable SSL auto certification via Let's Encrypt (and allow for multiple apps on one server).
# Set ssl: false if using something like Cloudflare to terminate SSL (but keep host!).
proxy:
ssl: false
# host: 52.146.91.48
# kamal-proxy connects to your container over port 80, use `app_port` to specify a different port.
app_port: 3000
healthcheck:
path: /up
# Credentials for your image host.
registry:
# Specify the registry server, if you're not using Docker Hub
# server: registry.digitalocean.com / ghcr.io / ...
server: xyz.azurecr.io
username: xyz
# Always use an access token rather than real password (pulled from .kamal/secrets).
password:
- KAMAL_REGISTRY_PASSWORD
# Configure builder setup.
builder:
# context: .
arch: amd64
# Inject ENV variables into containers (secrets come from .kamal/secrets).
#
env:
clear:
DB_HOST: <%= ENV["DB_HOST"] %>
secret:
- RAILS_MASTER_KEY
- KAMAL_REGISTRY_PASSWORD
- POSTGRES_PASSWORD
# Aliases are triggered with "bin/kamal <alias>". You can overwrite arguments on invocation:
# "bin/kamal logs -r job" will tail logs from the first server in the job section.
#
# aliases:
# shell: app exec --interactive --reuse "bash"
# Use a different ssh user than root
ssh:
user: azureuser
3.5 Update config/environments/production.rb
Comment out force_ssl
.
# config.force_ssl = true
3.6. Update config/routes.rb
root "posts#index"