<?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>DEV Community: Cyril Rohr</title>
    <description>The latest articles on DEV Community by Cyril Rohr (@crohr).</description>
    <link>https://dev.to/crohr</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%2F574062%2F3d9f8d80-fa34-47d1-9901-f1eeebf4690e.jpeg</url>
      <title>DEV Community: Cyril Rohr</title>
      <link>https://dev.to/crohr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/crohr"/>
    <language>en</language>
    <item>
      <title>How to verify that VPC traffic to S3 is going through your S3 gateway?</title>
      <dc:creator>Cyril Rohr</dc:creator>
      <pubDate>Fri, 02 Feb 2024 09:42:02 +0000</pubDate>
      <link>https://dev.to/runs-on/how-to-verify-that-vpc-traffic-to-s3-is-going-through-your-s3-gateway-4ab6</link>
      <guid>https://dev.to/runs-on/how-to-verify-that-vpc-traffic-to-s3-is-going-through-your-s3-gateway-4ab6</guid>
      <description>&lt;p&gt;Gateway endpoints for Amazon S3 are a must-have whenever your EC2 instances send and receive traffic from S3, because they allow the traffic to stay within the AWS network, hence better security, bandwidth, throughput, and costs. They can easily be created, and added to your VPC route tables.&lt;/p&gt;

&lt;p&gt;But how do you verify that traffic is indeed going through the S3 gateway, and not crossing the outer internet?&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;traceroute&lt;/code&gt;, you can probe the routes and see whether you are directly hitting the S3 servers (i.e. no intermediate gateway). In this example, the instance is running from a VPC located in &lt;code&gt;us-east-1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;traceroute &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 443 s3.us-east-1.amazonaws.com
traceroute to s3.us-east-1.amazonaws.com &lt;span class="o"&gt;(&lt;/span&gt;52.216.215.72&lt;span class="o"&gt;)&lt;/span&gt;, 30 hops max, 60 byte packets
 1  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 2  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 3  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 4  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 5  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 6  52.216.215.72  0.890 ms  0.916 ms  0.892 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;traceroute &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 443 s3.amazonaws.com
traceroute to s3.amazonaws.com &lt;span class="o"&gt;(&lt;/span&gt;52.217.139.232&lt;span class="o"&gt;)&lt;/span&gt;, 30 hops max, 60 byte packets
 1  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 2  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 3  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 4  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 5  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 6  52.217.139.232  0.268 ms  0.275 ms  0.252 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both outputs produce the expected result, i.e. no intermediary gateway. This is what would happen if you were accessing a bucket located in the &lt;code&gt;us-east-1&lt;/code&gt; region.&lt;/p&gt;

&lt;p&gt;Let's see what happens if we try to access an S3 endpoint located in another zone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;traceroute &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 443 s3.eu-west-1.amazonaws.com
traceroute to s3.eu-west-1.amazonaws.com &lt;span class="o"&gt;(&lt;/span&gt;52.218.25.211&lt;span class="o"&gt;)&lt;/span&gt;, 30 hops max, 60 byte packets
 1  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 2  240.4.88.37  0.275 ms 240.0.52.64  0.265 ms 240.4.88.39  0.215 ms
 3  240.4.88.49  0.205 ms 240.4.88.53  0.231 ms 240.4.88.51  0.206 ms
 4  100.100.8.118  1.369 ms 100.100.6.96  0.648 ms 240.0.52.57  0.233 ms
 5  240.0.228.5  0.326 ms &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 6  240.0.32.16  0.371 ms 240.0.48.30  0.362 ms &lt;span class="k"&gt;*&lt;/span&gt;
 7  &lt;span class="k"&gt;*&lt;/span&gt; 240.0.228.31  0.251 ms &lt;span class="k"&gt;*&lt;/span&gt;
 8  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
 9  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 240.0.32.27  0.392 ms
10  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
11  &lt;span class="k"&gt;*&lt;/span&gt; 242.0.154.49  1.321 ms &lt;span class="k"&gt;*&lt;/span&gt;
12  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 52.93.28.131  1.491 ms
13  &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 100.100.6.108  1.286 ms
14  100.92.212.7  67.909 ms 52.218.25.211  67.356 ms  67.929 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the route is completely different, and as expected does not hit straight to the S3 endpoint.&lt;/p&gt;

&lt;p&gt;TL;DR: make sure your route tables are correct, and only point to S3 buckets located in the same region.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints-s3.html"&gt;Gateway endpoints for Amazon S3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>s3</category>
      <category>aws</category>
      <category>vpc</category>
    </item>
    <item>
      <title>Introducing RunsOn: 10x cheaper GitHub Action runners</title>
      <dc:creator>Cyril Rohr</dc:creator>
      <pubDate>Tue, 23 Jan 2024 12:37:39 +0000</pubDate>
      <link>https://dev.to/runs-on/introducing-runson-10x-cheaper-github-action-runners-51dl</link>
      <guid>https://dev.to/runs-on/introducing-runson-10x-cheaper-github-action-runners-51dl</guid>
      <description>&lt;p&gt;Let's face it, sometimes you need faster execution of your GitHub Action workflows.&lt;/p&gt;

&lt;p&gt;I've worked for a few companies where the full test suite runtime was greater than 20min, and this is not good for developer feedback.&lt;/p&gt;

&lt;p&gt;The usual fix here is to deploy faster self-hosted runners, but then this means you must ensure that those runners stay online, regularly patched, and sufficiently used to justify their cost.&lt;/p&gt;

&lt;p&gt;Wouldn't it be nice if you could spawn self-hosted runners on demand, with a large choice of CPU/RAM configurations, and for cheap? This is what &lt;a href="https://runs-on.com"&gt;RunsOn&lt;/a&gt; provides, with up to 10x cheaper runners, and the widest choice of runner types on the market.&lt;/p&gt;

&lt;p&gt;RunsOn can be installed in your own AWS account, which means there is no middleman between your workflows and your runner. It also supports both x64 and arm64 architecture, and is a one-line change from your current workflow files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- runs-on: ubuntu-latest
&lt;/span&gt;&lt;span class="gi"&gt;+ runs-on: runs-on,runner=16cpu-linux,image=ubuntu22-full-x64
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever a new workflow starts, a runner is automatically provisioned, and will be terminated as soon as the workflow ends. You only pay the per-minute cost of the runner, and you can easily track your costs in your AWS Cost Explorer page.&lt;/p&gt;

&lt;p&gt;Some quick pricing comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;runner&lt;/th&gt;
&lt;th&gt;cpu&lt;/th&gt;
&lt;th&gt;$/min (spot)&lt;/th&gt;
&lt;th&gt;$/min (github)&lt;/th&gt;
&lt;th&gt;GitHub vs RunsOn&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1cpu-linux&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0.0008&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2cpu-linux&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0.0011&lt;/td&gt;
&lt;td&gt;0.008&lt;/td&gt;
&lt;td&gt;7x more expensive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;4cpu-linux&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0.0022&lt;/td&gt;
&lt;td&gt;0.016&lt;/td&gt;
&lt;td&gt;7x more expensive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;8cpu-linux&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;0.0035&lt;/td&gt;
&lt;td&gt;0.032&lt;/td&gt;
&lt;td&gt;9x more expensive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;16cpu-linux&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;0.0068&lt;/td&gt;
&lt;td&gt;0.064&lt;/td&gt;
&lt;td&gt;9x more expensive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;32cpu-linux&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;0.0132&lt;/td&gt;
&lt;td&gt;0.128&lt;/td&gt;
&lt;td&gt;10x more expensive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;48cpu-linux&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;td&gt;0.0170&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;64cpu-linux&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;0.0196&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The cost is fully open, can be installed in one click thanks to a cloudformation template, and a license only costs $250 once. Find out more at &lt;a href="https://runs-on.com"&gt;https://runs-on.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>githubactions</category>
      <category>cicd</category>
      <category>github</category>
    </item>
    <item>
      <title>Automated preview deployments from GitHub to AWS in 10 minutes</title>
      <dc:creator>Cyril Rohr</dc:creator>
      <pubDate>Mon, 12 Jun 2023 15:44:01 +0000</pubDate>
      <link>https://dev.to/crohr/automated-preview-deployments-from-github-to-aws-in-10-minutes-17k1</link>
      <guid>https://dev.to/crohr/automated-preview-deployments-from-github-to-aws-in-10-minutes-17k1</guid>
      <description>&lt;p&gt;Preview deployments are a way to spin temporary environments from branches or pull requests. They're used to allow teammates and product managers to visually review your work before changes are merged, without relying on code only.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 ways to get preview deployments
&lt;/h2&gt;

&lt;p&gt;To use preview deployments in your development workflow, you have 3 solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your hosting provider has preview deployments sorted out (e.g. Heroku / Vercel / Render / Scalingo). Easy to setup, but you get vendor lock-in, and some providers are costly.&lt;/li&gt;
&lt;li&gt;you find a 3rd-party SaaS to provide the service for you. Setup can be complicated if they use custom definition files, it's costly, and your code is now at risk in another provider's infrastructure.&lt;/li&gt;
&lt;li&gt;you build or use a solution that doesn't rely on 3rd-parties. This could be a GitHub Action, Jenkins pipeline, semi-automated script etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building a custom solution is costly in terms of development and maintenance, so today I want to talk about PullPreview, a GitHub Action which makes preview deployments very easy to setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  PullPreview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pullpreview.com"&gt;PullPreview&lt;/a&gt; is an &lt;a href="https://github.com/pullpreview/action"&gt;open-source GitHub Action&lt;/a&gt; which has the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;works with any app that can be booted with Docker Compose (i.e. all).&lt;/li&gt;
&lt;li&gt;can be triggered by applying a label on a pull request.&lt;/li&gt;
&lt;li&gt;deploys code directly from GitHub onto AWS (Lightsail).&lt;/li&gt;
&lt;li&gt;servers are started in your own AWS account.&lt;/li&gt;
&lt;li&gt;SSH access to the servers.&lt;/li&gt;
&lt;li&gt;you can use your own custom domain if you want.&lt;/li&gt;
&lt;li&gt;environments are persistent across deployments.&lt;/li&gt;
&lt;li&gt;fully integrated into GitHub UI, no need to go through another app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's get started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Add AWS credentials to your repo
&lt;/h3&gt;

&lt;p&gt;PullPreview requires AWS credentials to deploy servers, so let's &lt;a href="https://us-east-1.console.aws.amazon.com/iamv2/home?region=us-east-1#/users/create"&gt;create a new user&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wcN7XG8u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nuf7c13ohlzz64hgo7ko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wcN7XG8u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nuf7c13ohlzz64hgo7ko.png" alt="Create new AWS user for pullpreview" width="800" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For some obscure reason AWS doesn't provide a default policy for Lightsail, so you will need to create a new Policy for this user, with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "lightsail:*",
            "Resource": "*"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RYprN12p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggdd30t4oarrm0pj0ttw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RYprN12p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggdd30t4oarrm0pj0ttw.png" alt="Create new IAM policy for pullpreview user" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then attach the newly created policy and create the user:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lZydsqhE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98lutbm048o93cjh3s3l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lZydsqhE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98lutbm048o93cjh3s3l.png" alt="Attach policy and create user" width="800" height="1154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Final step is to retrieve an access key and secret access key for that new user, and declare those keys into your repository settings on GitHub, as new Repository Secrets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l1ugk36C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/voimn4j5v8u2kht5m5p7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l1ugk36C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/voimn4j5v8u2kht5m5p7.png" alt="Add credentials as repo secrets" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Label, workflow file, and example Docker Compose app
&lt;/h3&gt;

&lt;p&gt;The PullPreview action will be triggered only for pull requests that have a specific label applied. By default the label name is &lt;code&gt;pullpreview&lt;/code&gt;, so let's create it in your GitHub repository. Use any color you like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OE9LN_12--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7xcbnxqw749bcsij5e2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OE9LN_12--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7xcbnxqw749bcsij5e2.png" alt="Create pullpreview label on GitHub" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, create a new branch, and add the following files:&lt;/p&gt;

&lt;p&gt;(1) the pullpreview workflow file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/pullpreview.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PullPreview&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;labeled&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;unlabeled&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;synchronize&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;closed&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;reopened&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt; &lt;span class="c1"&gt;# to fetch code (actions/checkout)&lt;/span&gt;
      &lt;span class="na"&gt;deployments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt; &lt;span class="c1"&gt;# to delete deployments&lt;/span&gt;
      &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt; &lt;span class="c1"&gt;# to remove labels&lt;/span&gt;
      &lt;span class="na"&gt;statuses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt; &lt;span class="c1"&gt;# to create commit status&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pullpreview/action@v5&lt;/span&gt;
      &lt;span class="c1"&gt;# see https://github.com/pullpreview/action/wiki/Inputs&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;admins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-github-username&lt;/span&gt;
        &lt;span class="na"&gt;compose_files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker-compose.pullpreview.yml&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80,443&lt;/span&gt;
        &lt;span class="na"&gt;default_port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(2) an example docker-compose file, with SSL support:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.pullpreview.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# easily set up SSL termination&lt;/span&gt;
  &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;caddy:2&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;caddy&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;reverse-proxy&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'${PULLPREVIEW_URL}'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;web:4567"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/data"&lt;/span&gt;

  &lt;span class="c1"&gt;# simple ruby web server for demo purposes, which connects to a postgres DB&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile_inline&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;FROM ruby:3.2&lt;/span&gt;
        &lt;span class="s"&gt;RUN gem install sinatra pg puma&lt;/span&gt;
        &lt;span class="s"&gt;CMD ruby -rsinatra -rpg -rpuma \&lt;/span&gt;
          &lt;span class="s"&gt;-e'get("/"){ "Hey PullPreview user, here is the postgres version: #{PG.connect(ENV["PGURI"]).exec("SELECT VERSION()").getvalue(0,0)}" }'&lt;/span&gt;
        &lt;span class="s"&gt;EXPOSE 4567&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PGURI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres://postgres:p4ssw0rd@db/postgres"&lt;/span&gt;
      &lt;span class="na"&gt;APP_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;

  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:13&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;p4ssw0rd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Let the magic begin
&lt;/h3&gt;

&lt;p&gt;Now commit the previous files, push your new branch, and open a pull request with the &lt;code&gt;pullpreview&lt;/code&gt; label applied. GitHub will start executing the PullPreview job, and a new preview environment will be up and running a few minutes later!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HEB95Nqe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zdf8lnrcvnmvv7otmxbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HEB95Nqe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zdf8lnrcvnmvv7otmxbf.png" alt="Open PR and add label" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Zcg3AYnv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sw1c3yy4ngtfiww38kg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zcg3AYnv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sw1c3yy4ngtfiww38kg9.png" alt="Environment deploying" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qnF2D6Ah--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oydhkitcfgw8noxpy3dd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qnF2D6Ah--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oydhkitcfgw8noxpy3dd.png" alt="Environment deployed" width="800" height="686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c65kdckQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/95abm1aeimz8rxwz1n1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c65kdckQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/95abm1aeimz8rxwz1n1s.png" alt="Accessing the temporary environment" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Going further
&lt;/h2&gt;

&lt;p&gt;Next steps would be to replace the example &lt;code&gt;docker-compose.ci.yml&lt;/code&gt; file with your own Docker Compose file, and see if the environment gets successfully deployed as well! You might need a few attempts to get to the point where your environment is fully setup, but remember that you can always SSH into the temporary server if the need arises!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've only scratched the surface of what PullPreview can do. The fact that it runs entirely within the GitHub ecosystem and in your own AWS account allows for a wide variety of use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;access restriction from specific IP ranges&lt;/li&gt;
&lt;li&gt;custom domains&lt;/li&gt;
&lt;li&gt;various instance types&lt;/li&gt;
&lt;li&gt;support for private docker registries&lt;/li&gt;
&lt;li&gt;support for multiple docker compose files&lt;/li&gt;
&lt;li&gt;support for multiple preview environments per pull request&lt;/li&gt;
&lt;li&gt;support for seed data&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should definitely have a look at the &lt;a href="https://github.com/pullpreview/action/wiki"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also note that the PullPreview action is free to use for personal use, but comes with a commercial license (300€/year) for businesses. This ensures that the action is maintained and new features added.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>githubactions</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
