<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Marco Dell'Olio</title>
    <description>The latest articles on Forem by Marco Dell'Olio (@xantrac).</description>
    <link>https://forem.com/xantrac</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F194387%2F24decacd-c0c2-4330-850b-5db31bed4862.jpg</url>
      <title>Forem: Marco Dell'Olio</title>
      <link>https://forem.com/xantrac</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/xantrac"/>
    <language>en</language>
    <item>
      <title>Connect your Fly.io app to AWS RDS Postgres. Terraform complete walkthrough.</title>
      <dc:creator>Marco Dell'Olio</dc:creator>
      <pubDate>Mon, 12 Feb 2024 18:22:14 +0000</pubDate>
      <link>https://forem.com/xantrac/connect-your-flyio-app-to-aws-rds-postgres-terraform-complete-walkthrough-5fgb</link>
      <guid>https://forem.com/xantrac/connect-your-flyio-app-to-aws-rds-postgres-terraform-complete-walkthrough-5fgb</guid>
      <description>&lt;p&gt;I built &lt;a href="//choremate.co"&gt;choremate.co&lt;/a&gt; with Phoenix and Elixir and the preferred PaaS for the stack nowadays is &lt;a href="//fly.io"&gt;Fly.io&lt;/a&gt;, great CLI, and generous free tier. (Bonus, the creator of the Phoenix Framework works there.)&lt;/p&gt;

&lt;p&gt;While their hosting and deployment experience is great, they do not offer a managed database option, so I had to look elsewhere: the good 'ol AWS RDS (Postgres). &lt;/p&gt;

&lt;p&gt;I documented in this post all the necessary steps to use Terraform to create a Postgres instance on AWS and run an EC2 host with PGBouncer and Wireguard to connect your Fly.io app to AWS VPN through a tunnel.&lt;/p&gt;

&lt;p&gt;This guide starts from this (repo)[&lt;a href="https://github.com/fly-apps/rds-connector?tab=readme-ov-file"&gt;https://github.com/fly-apps/rds-connector?tab=readme-ov-file&lt;/a&gt;] which at this point is outdated in some of their parts but still a very good starting point.&lt;/p&gt;

&lt;p&gt;Overall we will be creating a Postgres instance not publicly accessible, an ec2 instance that will work as a bastion with PgBouncer functioning as a proxy and WireGuard installed so we'll be able to establish a tunnel between the Fly.io private network and our Aws private network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rzkjpqe53njgqljj4w7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rzkjpqe53njgqljj4w7.png" alt="Image description" width="800" height="376"&gt;&lt;/a&gt;&lt;em&gt;Should look something like this&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This guide will also make a couple of assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you already have a web application running on Fly.io and an - you already have an active account on AWS&lt;/li&gt;
&lt;li&gt;you are working on a Mac Silicon machine (mostly impacts how you will install dependencies)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Required dependencies&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install terraform
brew install awscli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 0.5 - AWS local Authentication&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Not required but strongly suggested &lt;a href="https://github.com/99designs/aws-vault"&gt;https://github.com/99designs/aws-vault&lt;/a&gt; allows you to manage your AWS credential on your local machine.&lt;/p&gt;

&lt;p&gt;The instructions are pretty straightforward, but in case you need them, here is the &lt;a href="https://us-east-1.console.aws.amazon.com/iam/home?region=us-east-1#/security_credentials/access-key-wizard"&gt;link&lt;/a&gt; is where you create you can quickly create a root access key (I know you should use dedicated identity but I can't cover everything here)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 - Create SSH key-pair to use in our VPC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We'll reference those keys in our Terraform file so they will be used inside our VPC and will use it to ssh into our EC2 instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ ssh-keygen -b 4096 -t rsa

Generating public/private rsa key pair.
Enter file in which to save the key (/Users/me/.ssh/id_rsa): aws_terraform
Enter passphrase (empty for no passphrase): #nothing here

❯ ls ~/.ssh
aws_terraform           aws_terraform.pub       config 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2 - Generate the Wireguard configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Wireguard will be used to generate a VPN tunnel between the EC2 instance and the Fly.io VPN, enabling our app to send requests to our database without using the Internet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fly wireguard create
flyctl wireguard create
? Select Organization:  [Use arrows to move, type to filter]
&amp;gt; ChoreMate (choremate)
❯
❯
❯  flyctl wireguard create choremate iad mypostgrespeer wg0.conf
? Select Organization: ChoreMate (choremate)
Creating WireGuard peer "mypostgrespeer" in region "iad" for organization choremate

!!!! WARNING: Output includes private key. Private keys cannot be recovered !!!!
!!!! after creating the peer; if you lose the key, you'll need to remove    !!!!
!!!! and re-add the peering connection.                                     !!!!
? Filename to store WireGuard configuration in, or 'stdout':

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will use this file in a moment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 - Clone and customize the demo configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We'll keep everything in a single &lt;code&gt;.tf&lt;/code&gt; file, but feel free to learn how to split a Terraform project into different files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone git@github.com:xantrac/rds-fly-connector.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the folder has the following structure&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── main.tf 
├── pgbouncer.ini.tmpl
├── terraform.tfvars
└── userlist.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main.tf&lt;/code&gt; includes all the Terrform instructions to create our infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pgbouncer.ini.tmpl&lt;/code&gt; is a template for pgbouncer configuration to run in our ec2 instance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform.tfvars&lt;/code&gt; contains some variables that you will personalize&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;userlist.txt&lt;/code&gt; is a list of postgres users that will be created with the database, defaults to &lt;code&gt;postgres&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3.1 Open the &lt;code&gt;terraform.tfvars&lt;/code&gt; file and edit the existing placeholders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws_account = "cool-app"
vpc_name = "cool-app-vpc"
postgres_instance_name = "cool-app-prod"
database_name = "cool-app"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.2 Copy your &lt;code&gt;wg0.conf&lt;/code&gt; file to the root of the &lt;code&gt;rds-fly-connector&lt;/code&gt; directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cp ~/wg0.conf ~/rds-fly-connector #or wherever your files are located
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, your Terraform file is ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 - Apply the terraform configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Will split this into two steps, first will &lt;em&gt;plan&lt;/em&gt; the configuration.&lt;/p&gt;

&lt;p&gt;From within the &lt;code&gt;rds-fly-connector&lt;/code&gt; directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws-vault exec choremate -- terraform plan -out prod.tfstate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will analyze the configuration, fill in the templated variables, generate the final configuration object, and generate a &lt;code&gt;.tfstate&lt;/code&gt; file which is the artifact of how our infrastructure will look at a given time.&lt;/p&gt;

&lt;p&gt;Then will apply the &lt;code&gt;.tfstate&lt;/code&gt; file to create our resources in AWS&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws-vault exec choremate -- terraform apply prod.tfstate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, (likely 10 minutes later) Terraform is done applying the configuration. Let's verify the result.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;your database should be available at the following link &lt;a href="https://us-east-1.console.aws.amazon.com/rds/home?region=us-east-1#databases:"&gt;https://us-east-1.console.aws.amazon.com/rds/home?region=us-east-1#databases:&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;your ec2 bastion should be here &lt;a href="https://us-east-1.console.aws.amazon.com/ec2/home?region=us-east-1#Instances:instanceState=running"&gt;https://us-east-1.console.aws.amazon.com/ec2/home?region=us-east-1#Instances:instanceState=running&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then click on &lt;em&gt;connect&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj9d0wx7i4qspljefteg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj9d0wx7i4qspljefteg.png" alt="Image description" width="800" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and copy the public address&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4pyl24a30c8cfb7ral5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4pyl24a30c8cfb7ral5.png" alt="Image description" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;use the public address and the ssh key created in step 1 to connect to the bastion&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯  ssh -i ~/.ssh/aws_terraform  ubuntu@ec2-52-202-142-228.compute-1.amazonaws.com
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 6.2.0-1018-aws x86_64)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and attempt to connect to your database, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;username which is &lt;code&gt;postgres&lt;/code&gt; unless you changed it&lt;/li&gt;
&lt;li&gt;password, can be found here &lt;a href="https://us-east-1.console.aws.amazon.com/secretsmanager/listsecrets?region=us-east-1"&gt;https://us-east-1.console.aws.amazon.com/secretsmanager/listsecrets?region=us-east-1&lt;/a&gt; (dig a little, should be easy)&lt;/li&gt;
&lt;li&gt;database address, on your database page &lt;a href="https://us-east-1.console.aws.amazon.com/rds/home?region=us-east-1#databases:"&gt;https://us-east-1.console.aws.amazon.com/rds/home?region=us-east-1#databases:&lt;/a&gt; under &lt;em&gt;Connectivity &amp;amp; security&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4l63hmqmy1rkvpg9iz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4l63hmqmy1rkvpg9iz8.png" alt="Image description" width="670" height="822"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;putting al together we should have our postgres URL that looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;psql postgres://postgres:#{password}@#{database_url}:5432/#{database_name}

ubuntu@ip-172-16-101-225:~$ psql postgres://postgres:#########@############.us-east-1.rds.amazonaws.com:5432/chore_mate
psql (14.10 (Ubuntu 14.10-0ubuntu0.22.04.1), server 15.5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see this everything is working, so far!&lt;/p&gt;

&lt;p&gt;Let's check if WireGuard is working correctly when attempting to access the Postgres instance from within your Fly app.&lt;/p&gt;

&lt;p&gt;When you create the WireGuard configuration in step 2 you also register the named peer within the Fly registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fly wireguard list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the name you assigned to your configuration should be listed there.&lt;/p&gt;

&lt;p&gt;Now because our ec2 bastion is running WireGuard and PgBouncer as a proxy our database should be reachable through that host at the &lt;code&gt;#{peer_name}._peer.internal&lt;/code&gt; and we should be able to compose our postgres URL in this way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgres://postgres:#{password}@#{peer_name}._peer.internal:5432/#{db_name} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fly ssh console

apt update
apt upgrade
apt install -y postgresql-client

 psql postgres://postgres:#{password}@#{peer_name}._peer.internal:5432/#{db_name}

psql (14.10 (Ubuntu 14.10-0ubuntu0.22.04.1), server 15.5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If even this step worked you are done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5 - Add the DATABASE_URL env to your app&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ fly secrets set DATABASE_URL=postgres://postgres:#{password}@#{peer_name}._peer.internal:5432/#{db_name} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enjoy your new AWS database!&lt;/p&gt;

&lt;p&gt;P.S. Please let me know if you encounter any hiccups in the process so I can keep this guide updated.&lt;/p&gt;

</description>
      <category>fly</category>
      <category>postgres</category>
      <category>aws</category>
      <category>elixir</category>
    </item>
  </channel>
</rss>
