In this episode, we setup AWS infrastructure: VPC & subnets, Bastion Host and RDS with PostGIS.

List of Posts:

  1. part 1: Local server setup
  2. part 2: Basic Rails App with PostGIS
  3. part 3: Docker
  4. part 4: AWS setup
  5. part 5: AWS setup & deploy



  1. Introduction
  2. VPC & subnets
  3. Bastion Host
  4. RDS + PostgreSQL + PostGIS


1. Introduction

The goal is to run Rails API application on Elastic Beanstalk. Rails API application (running in Elastic Beanstalk service) consists of:

  • NGINX (as web server)
  • Rails (with Puma as app server) Docker containers.

Rails app has an API which will be then exposed by API Gateway. Rails Application will be also connected with PostgreSQL + PostGIS running RDS. To make infrastructure more secure, we will disable the SSH logins from the Internet and leave open to only HTTP connections. Nevertheless, we still need to log in sometimes to specific instances to investigate what’s going on or to make additional setup. For this purpose, we will create a Bastion Host and make other instances available for Bastion Host. It allows us to log in via SSH to BH and then futher to other AWS instances.

rails api on aws - aws arch


  • Public subnet – contains Elastic Beanstalk and Bastion Host. It’s resources have are available from the Internet. It’s important that Elastic Beanstalk’s instances are running in public subnet, because it will be bound to API Gateway and API Gateway can talk only through the public endpoints.
  • Private subnet – to make our date safer, I place RDS with PostgreSQL and PostGIS in that subnet. We cannot talk directly with resources in private subnet from the Internet. But we can login to them through Bastion Host.
  • Bastion Host – is used to login to other AWS resources (like EC2 instances) also in private subnets.


2. VPC & subnets


Elastic IP

  1. Login to the AWS console (in Web Browser)
  2. Choose right region in top-right corner (e.g US East (Ohio), EU (Ireland))
  3. Click on Services in top-left corner and find VPC
  4. Go to Elastic IPs tab
  5. Click Allocate new address
  6. Allocate
  7. New Elastic IP should be created

VPC & subnets

  1. In VPC Dashboard, click Start VPC Wizard
  2. Select VPC with Public and Private Subnets tab and click Select
  3. Enter VPC name – I use “TestEnv”. Optionally, You can also adjust CIDR blocks and other settings.
  4. In Elastic IP Allocation ID select Elastic IP created in Elastic IP step.
  5. Click Create VPC
  6. AWS should generate new VPC with public & private subnets, NAT Gateways, Route tables etc.
  7. Optionally you can review auto-generated VPC components and add names. Below a list of components to review:
    1. Subnets
    2. Route Tables
    3. Internet Gateway
    4. NAT Gateways


3. Bastion Host

Create Bastion Host

  1. Click on Services and go to EC2
  2. Open Instances > Instances tab and click Launch Instance:
    1. Step 1: Choose image as you like – it doesn’t have any difference. I take Amazon Linux AMI.
    2. Step 2: Choose t2.micro or t2.nano instance type. Click: Next: Configure Instance Details
    3. Step 3: Select following settings:
      1. Network: VPC you’ve created
      2. Subnet: Public subnet – subnet-xxxx | Public subnet | region-name
      3. Auto-assign Public IP: Enable
      4. Click Next: Add Storage
    4. Step 4: Add storage – we can skip it and go further
    5. Step 5: Add tag:
      Value=Bastion Host
    6. Step 6: Configure Security Group
      1. “Create a new security group”
      2. Security group name: bastion-host-security-group
      3. In table should be one Rule:
        Type: SSH
        Source: Custom
      4. Click Review and Launch
    7. Step 7a: Click on Launch and add a new key pair. Then download generate key pair (*.pem)
    8. Step 7b: Click Launch instances


Create a test instance

  1. Launch new EC2 instance
  2. Select any AMI
  3. You can take t2.micro in 2nd step
  4. Step3: Instance details
    1. Network: VPC you’ve created
    2. Subnet: Private subnet – subnet-xxxx | Private subnet | region-name
    3. Auto-assign Public IP: Disable
  5. In next steps you can use default settings
  6. Step 7: Launch instance – there select a key-pair generated for Bastion Host.
  7. Launch instance


Login to Bastion Host

Change /path/to/key on the path to downloaded key-pair (Create Bastion Host > 2 > 7. Step 7a).
Change also xx.xx.xxx.xxx on the IP of your Bastion Host instance. You can find it in IPv4 Public IP column on the list of EC2 instances.
In last line, change on IP of your test instance. You can look up its IP on the list of EC2 instances in column Private IP address (or in details when you select specific instance).
If you used Ubuntu as a Bastion Host OS, change also user: ec2-user on ubuntu.

Now you can delete test instance. You can also stop Bastion Host and launch only when is needed.


4. RDS + PostgreSQL + PostGIS

Create RDS Instance

  1. Click on Services and go to RDS
  2. Tab InstancesLaunch Instance
  3. Step 1: Select PostgreSQL
  4. Step 2: Select Dev/Test
  5. Step 3: Specify DB Details:
    1. DB Engine Version: PostgreSQL 9.6.3-R1
    2. DB Instance Class: db.t2.micro should be enough for now
    3. Multi-AZ Deployment: for dev/test use No, but fo rproduction selecte Yes
    4. Allocated Storage: 5GB
    5. Settings – fill as you prefer, but don’t forget what have you set ther. We will need it in the future.
  6. Step 4: Configure Advanced Settings:
    1. VPC: Use VPC created in chapter 2
    2. Create new DB Subnet Group
    3. Publicly Accessible: No
    4. Availability Zone: No preference
    5. VPC Security Group: Create New Security Group
    6. Database Name: here put a name of your database. It’s important that it should be the same as in database.yml file in your Rails project, e.g: raow_app_production
    7. Click Launch DB Instance

Skip “Add more subnets” if don’t you get an error: DB Subnet Group doesn’t meet availability zone coverage requirement. We need to add more subnets manually.

Add more subnets

  1. Go to Services > VPC > Subnets tab
  2. Check what is the region of already created subnets
  3. Click Create Subnet:
    1. Name: Public subnet – zone 1b
    2. VPC – choose created VPC
    3. Availability Zone: select one, but different than have already created subnets
    4. IPv4 CIDR:
  4. Repeat step 3 to create the same but private subnet:
    1. Name: Private subnet – zone 1b
    2. VPC – choose created VPC
    3. Availability Zone: select one, but different than have already created subnets
    4. IPv4 CIDR:
  5. Open Route Tables tab
    1. Find route table, where one public subnet is assigned and assign another one


Add access for Bastion Host

  1. RDS Dashboard  > select created instance
  2. Find in instance details, under Security and NetworkSecurity Groups field. Click on it.
  3. You will be redirected where you should see security group of RDS instance.
  4. Choose Inbound tab and click Edit:
    1. Add Rule:
      Type: PostgreSQL
      Source: <bastion host security group>
    2. Save


Add PostGIS

Following instructions are based on AWS doc.

  1. Log in to Bastion Host (see chapter 3: Bastion Host)
  2. From Bastion Host, log in to Postgres on RDS

    Replace with your data.
  3. Add PostGIS extensions:
  4. Transfer Ownership of the Extensions to the rds_superuser Role
  5. Transfer Ownership of the Objects to the rds_superuser Role
  6. Test the Extensions


Tomasz Antas

Ruby on Rails developer and Web designer.
The area of his interest includes Popular Science, Internet of Things, Wearables, AI and Virtual/Augmented Reality.

Latest posts by Tomasz Antas (see all)

Thanks for reading!