<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Fly</title>
  <subtitle>Blog posts and updates from the team at Fly</subtitle>
  <id>https://fly.io/</id>
  <link href="https://fly.io/"/>
  <link href="https://fly.io/feed.xml" rel="self"/>
  <updated>2020-08-26T01:00:00+01:00</updated>
  <author>
    <name>Fly</name>
  </author>
  <entry>
    <title>Flyctl Builtins - The Fly Changelog for August</title>
    <link rel="alternate" href="https://fly.io/blog/flyctl-builtins-the-fly-changelog-for-august/"/>
    <id>https://fly.io/blog/flyctl-builtins-the-fly-changelog-for-august/</id>
    <published>2020-08-26T01:00:00+01:00</published>
    <updated>2020-08-26T14:13:13+01:00</updated>
    <author>
      <name>Dj Walker-Morgan</name>
    </author>
    <content type="html">&lt;p class="lead"&gt;
Super-simple builtin builders and smart certificates creation - it&amp;#39;s all in the latest flyctl (v0.0.139) available now for your command line. Find out more about it in the Changelog.&lt;/p&gt;&lt;p&gt;For the latest version of flyctl, we&amp;#39;ve focussed on making your life fast and simple. From getting your first deployment up and running to setting up a host&amp;#39;s certificate.&lt;/p&gt;
&lt;h2 id='builtins' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#builtins'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Builtins&lt;/h2&gt;&lt;p&gt;First up, we&amp;#39;ve got the new “builtin” builders which you can select when you init your App. We have builtin builders for Node, Ruby, Deno, Go and there&amp;#39;s also a static web server available. No need for a Dockerfile, Just init and deploy.&lt;/p&gt;
&lt;p&gt;You can find out more at the command line with &lt;code&gt;flyctl builtins list&lt;/code&gt; which will give you information on all of the builtins, and &lt;code&gt;flyctl builtins show &amp;lt;builtin-name&amp;gt;&lt;/code&gt; which will give you all the details, including the virtual Dockerfile that the builtins use.&lt;/p&gt;
&lt;p&gt;With builtins all you need to do is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write your code
&lt;/li&gt;&lt;li&gt;Run &lt;code&gt;flyctl init&lt;/code&gt;
&lt;/li&gt;&lt;li&gt;Select a builtin for the code&amp;#39;s language, hit return for defaults otherwise
&lt;/li&gt;&lt;li&gt;Run &lt;code&gt;flyctl deploy&lt;/code&gt;
&lt;/li&gt;&lt;li&gt;Finally, run &lt;code&gt;flyctl open&lt;/code&gt; to view your app in a browser.
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;You can see full examples for &lt;a href="/docs/getting-started/golang/"&gt;Go&lt;/a&gt;, &lt;a href="/docs/getting-started/node/"&gt;Node&lt;/a&gt;, &lt;a href="/docs/getting-started/ruby/"&gt;Ruby&lt;/a&gt;, &lt;a href="/docs/getting-started/deno/"&gt;Deno&lt;/a&gt; and &lt;a href="/docs/getting-started/static/"&gt;a static web server&lt;/a&gt; in the documentation.&lt;/p&gt;
&lt;h2 id='images' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#images'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Images&lt;/h2&gt;&lt;p&gt;In previous versions of &lt;code&gt;flyctl&lt;/code&gt;, we have been able to deploy an image from the command line. In this version, you can now use the Image builder (or &lt;code&gt;--image&lt;/code&gt; flag on &lt;code&gt;flyctl init&lt;/code&gt;) to select a public Docker image to be published to Fly. This persists the selected image in a &lt;code&gt;fly.toml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;By using the &lt;code&gt;fly.toml&lt;/code&gt; file, it also lets you configure the ports and health checks for the image. Best of all, it lets you save that information into your git repository so you can perform repeatable builds with images.&lt;/p&gt;
&lt;h2 id='backup-regions' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#backup-regions'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Backup Regions&lt;/h2&gt;&lt;p&gt;When, for whatever reason, an App instance is unable to deploy in a particular region, Fly will look to deploying it in a backup region. This is a longstanding behavior of Fly, but flyctl has been not been good in communicating that that is what is happening, leading to confusion over why an App is deploying in a region not in the region list.&lt;/p&gt;
&lt;p&gt;Not anymore! &lt;code&gt;flyctl regions list&lt;/code&gt; will now show the regions and the associated backup regions that an app instance may appear in. Also, &lt;code&gt;flyctl status&lt;/code&gt; will display &lt;code&gt;(b)&lt;/code&gt; net to any region which is not in the region pool and therefore a backup region. This should give everyone a better view of where their app is running.&lt;/p&gt;
&lt;h2 id='guided-certs' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#guided-certs'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Guided Certs&lt;/h2&gt;&lt;p&gt;In the past, when creating a certificate for a hostname, you had to refer to the documentation or UI for the various steps you needed to take. In this version of &lt;code&gt;flyctl&lt;/code&gt;, we&amp;#39;ve embedded some smart-documentation into the process. When you add a certificate now, &lt;code&gt;flyctl&lt;/code&gt; will give you detailed instructions on what you need to set with your DNS provider to direct traffic to your App through your domain name and verify your ownership (allowing a certificate to be generated). It&amp;#39;s all part of making &lt;code&gt;flyctl&lt;/code&gt; your preferred way to work with Fly.&lt;/p&gt;
&lt;h2 id='orgs-and-dns' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#orgs-and-dns'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Orgs and DNS&lt;/h2&gt;&lt;p&gt;Also implemented in 0.0.138, are orgs and dns commands. Orgs allows you to add and remove organizations - which you can also do from the Web UI. What you currently can&amp;#39;t do is invite and remove users from the organization; you&amp;#39;ll have to use the Web UI for that. This, and the dns command, are works in progress and could change in a later version. We thought it was better to let you see what was in the longer pipeline.&lt;/p&gt;
&lt;h2 id='other-changes' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#other-changes'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Other changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;flyctl init&lt;/code&gt; now has an &lt;code&gt;--import&lt;/code&gt; option which will use the settings of the imported &lt;code&gt;fly.toml&lt;/code&gt; file while creating a new app name and slot.
&lt;/li&gt;&lt;li&gt;JSON status output now has timestamps.
&lt;/li&gt;&lt;li&gt;&lt;code&gt;flyctl deploy&lt;/code&gt; now has a --local-only flag to force builds to happen locally or not at all. 
&lt;/li&gt;&lt;/ul&gt;
&lt;p class="callout"&gt;
This is the Fly Changelog where we list all significant changes to the Fly platform, tooling and web sites. You can also use the RSS feed of just changelog posts available on &lt;a href="https://fly.io/changelog.xml"&gt;fly.io/changelog.xml&lt;/a&gt; or consult our dedicated &lt;a href="https://fly.io/changelog/"&gt;ChangeLog&lt;/a&gt; page with all the recent updates.&lt;/p&gt;&lt;!-- start --&gt;
&lt;h2 id='25th-august' class='relative flex ai:baseline mt:5 mb:3 pb:8p bb active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pb:8p pr:8p' aria-hidden='' href='#25th-august'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;25th August&lt;/h2&gt;&lt;p&gt;&lt;strong class='text:dark-gray'&gt;flyctl&lt;/strong&gt;: Version &lt;a href="https://github.com/superfly/flyctl/releases/tag/v0.0.139"&gt;0.0.139 released&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li class='changelog-item bg:lightest-blue text:dark-blue anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M18.217 8.658h-7.652M13.435 15.353H5.783M15.348 11.527l2.87-2.87-2.87-2.869M8.652 18.223l-2.87-2.87 2.87-2.87' /&gt;&lt;path d='M23 12.006c0 6.073-4.926 11-11 11-6.076 0-11-4.927-11-11 0-6.074 4.924-11 11-11 6.074 0 11 4.926 11 11z' /&gt;&lt;/svg&gt; fix issue with builtin init
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;strong class='text:dark-gray'&gt;Fly Platform/Web&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li class='changelog-item bg:lightest-blue text:dark-blue anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M18.217 8.658h-7.652M13.435 15.353H5.783M15.348 11.527l2.87-2.87-2.87-2.869M8.652 18.223l-2.87-2.87 2.87-2.87' /&gt;&lt;path d='M23 12.006c0 6.073-4.926 11-11 11-6.076 0-11-4.927-11-11 0-6.074 4.924-11 11-11 6.074 0 11 4.926 11 11z' /&gt;&lt;/svg&gt; Updated documentation in sync with new flyctl.
&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id='25th-august-2' class='relative flex ai:baseline mt:5 mb:3 pb:8p bb active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pb:8p pr:8p' aria-hidden='' href='#25th-august-2'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;25th August&lt;/h2&gt;&lt;p&gt;&lt;strong class='text:dark-gray'&gt;flyctl&lt;/strong&gt;: Version &lt;a href="https://github.com/superfly/flyctl/releases/tag/v0.0.138"&gt;0.0.138 released&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added flyctl builtins and &lt;code&gt;flyctl builtins list&lt;/code&gt;.
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added &lt;code&gt;flyctl builtins show&lt;/code&gt;.
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added images as an option to builder selection.
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added --image as flag to &lt;code&gt;flyctl init&lt;/code&gt;
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added prompting to &lt;code&gt;certs&lt;/code&gt; process to give directions to user on next steps after adding or checking a certificate.
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Changed certs create/delete to add/remove (aliased for back compatibilty)
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added prompting for internal port to &lt;code&gt;flyctl init&lt;/code&gt;.
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added display of backup regions.
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added &lt;code&gt;--import&lt;/code&gt; to &lt;code&gt;flyctl&lt;/code&gt;.
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added &lt;code&gt;--local-only&lt;/code&gt; option to &lt;code&gt;flyctl deploy&lt;/code&gt;.
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added &lt;code&gt;orgs&lt;/code&gt; and &lt;code&gt;dns&lt;/code&gt; commands.
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Displays app URL before deployment.
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; JSON status output now has timestamp.
&lt;/li&gt;&lt;li class='changelog-item bg:lightest-blue text:dark-blue anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M18.217 8.658h-7.652M13.435 15.353H5.783M15.348 11.527l2.87-2.87-2.87-2.869M8.652 18.223l-2.87-2.87 2.87-2.87' /&gt;&lt;path d='M23 12.006c0 6.073-4.926 11-11 11-6.076 0-11-4.927-11-11 0-6.074 4.924-11 11-11 6.074 0 11 4.926 11 11z' /&gt;&lt;/svg&gt; Caught errors in suspend (thanks @alrs).
&lt;/li&gt;&lt;li class='changelog-item bg:lightest-blue text:dark-blue anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M18.217 8.658h-7.652M13.435 15.353H5.783M15.348 11.527l2.87-2.87-2.87-2.869M8.652 18.223l-2.87-2.87 2.87-2.87' /&gt;&lt;path d='M23 12.006c0 6.073-4.926 11-11 11-6.076 0-11-4.927-11-11 0-6.074 4.924-11 11-11 6.074 0 11 4.926 11 11z' /&gt;&lt;/svg&gt; Clarified monitoring error messages.
&lt;/li&gt;&lt;li class='changelog-item bg:lightest-blue text:dark-blue anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M18.217 8.658h-7.652M13.435 15.353H5.783M15.348 11.527l2.87-2.87-2.87-2.869M8.652 18.223l-2.87-2.87 2.87-2.87' /&gt;&lt;path d='M23 12.006c0 6.073-4.926 11-11 11-6.076 0-11-4.927-11-11 0-6.074 4.924-11 11-11 6.074 0 11 4.926 11 11z' /&gt;&lt;/svg&gt; Only display auth URL when an error has occurred.
&lt;/li&gt;&lt;li class='changelog-item bg:lightest-blue text:dark-blue anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M18.217 8.658h-7.652M13.435 15.353H5.783M15.348 11.527l2.87-2.87-2.87-2.869M8.652 18.223l-2.87-2.87 2.87-2.87' /&gt;&lt;path d='M23 12.006c0 6.073-4.926 11-11 11-6.076 0-11-4.927-11-11 0-6.074 4.924-11 11-11 6.074 0 11 4.926 11 11z' /&gt;&lt;/svg&gt; Setting secret to same value no longer causes a &amp;quot;no deployment available&amp;quot; error.
&lt;/li&gt;&lt;li class='changelog-item bg:lightest-blue text:dark-blue anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M18.217 8.658h-7.652M13.435 15.353H5.783M15.348 11.527l2.87-2.87-2.87-2.869M8.652 18.223l-2.87-2.87 2.87-2.87' /&gt;&lt;path d='M23 12.006c0 6.073-4.926 11-11 11-6.076 0-11-4.927-11-11 0-6.074 4.924-11 11-11 6.074 0 11 4.926 11 11z' /&gt;&lt;/svg&gt; Improved internal API use.
&lt;/li&gt;&lt;li class='changelog-item bg:lightest-blue text:dark-blue anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M18.217 8.658h-7.652M13.435 15.353H5.783M15.348 11.527l2.87-2.87-2.87-2.869M8.652 18.223l-2.87-2.87 2.87-2.87' /&gt;&lt;path d='M23 12.006c0 6.073-4.926 11-11 11-6.076 0-11-4.927-11-11 0-6.074 4.924-11 11-11 6.074 0 11 4.926 11 11z' /&gt;&lt;/svg&gt; Immediate duplicates in logs supressed in client.
&lt;/li&gt;&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Sandboxing and Workload Isolation</title>
    <link rel="alternate" href="https://fly.io/blog/sandboxing-and-workload-isolation/"/>
    <id>https://fly.io/blog/sandboxing-and-workload-isolation/</id>
    <published>2020-07-29T01:00:00+01:00</published>
    <updated>2020-08-04T13:29:55+01:00</updated>
    <author>
      <name>Thomas Ptacek</name>
    </author>
    <content type="html">&lt;p&gt;Workload isolation makes it harder for a vulnerability in one service to compromise every other part of the platform. It has a long history going back to 1990s qmail, and we generally agree that it’s a good, useful thing.&lt;/p&gt;
&lt;p&gt;Despite a plethora of isolation options, in the time I spent consulting for technology companies I learned that the most common isolation mechanism is “nothing”. And that makes some sense! Most services are the single tenant of their deployment environment, or at least so central to the logical architecture that there’s nothing to meaningfully isolate them from. Since isolation can be expensive, and security is under-resourced generally, elaborate containment schemes are often not high up on the list of priorities.&lt;/p&gt;
&lt;p&gt;That logic goes out the window when you’re hosting other people’s stuff. Fly.io is a content delivery network for Docker containers. We make applications fast by parking them close to their users; we do that by running bare metal servers in a bunch of data centers around the world, and knitting them together with a global WireGuard mesh. Fly.io is extremely easy to play with — single-digit minutes to get your head around, and rather than talk about it, I’ll just suggest you grab a free account and try it.&lt;/p&gt;
&lt;p&gt;Meanwhile, I’m going to rattle off a bunch of different isolation techniques. I’ll spoil the list for you now: we use Firecracker, the virtualization engine behind Amazon’s Lambda and Fargate services. But the solution space we chose Firecracker from is interesting, and so you’re going to hear about it.&lt;/p&gt;
&lt;h3 id='chroot' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#chroot'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;chroot&lt;/h3&gt;&lt;p&gt;People like to say “chroot isn’t a security boundary”, but of course that isn’t really true, it’s just not very strong by itself. Chroot is the original sandboxing technique.&lt;/p&gt;
&lt;p&gt;The funniest problem with chroot is how it’s implemented: in the kernel process table, every struct proc (I was raised on BSD) has a pointer to its current working directory and to its root directory. The root directory is “enforced” when you try to cd to “..”; if your current working directory is already the root, the kernel won’t let “..” go below it. But when you call chroot(2), you don’t necessarily change directories; if you’re “above” your new root, the kernel will never see that new root in a path traversal.&lt;/p&gt;
&lt;p&gt;The real problem, of course, is the kernel attack surface. We don’t need to get cute yet; by itself, considering no other countermeasures, chroot gives you ptrace, procfs, device nodes, and, of course, the network.&lt;/p&gt;
&lt;p&gt;You shake a lot of these problems off by not running anything as “root”, but not everything.  A quick-but-important aside: in real-world attacks, the most important capability you can concede to an attacker is access to your internal network. It’s for the same reason that SSRF vulnerabilities (“unexpected HTTP proxies”) are almost always game-over, even though at first blush they might not seem much scarier than an unchecked redirect: there will be something you can aim an internal HTTP request to that will give an attacker code execution. Network access in a chroot jail is like that, but far more flexible.&lt;/p&gt;
&lt;p&gt;This problem will loom over almost everything I write about here; just keep it in mind.&lt;/p&gt;
&lt;p&gt;chroot is a popular component in modern sandboxes, but none of them really rely on it exclusively.&lt;/p&gt;
&lt;h3 id='privilege-separation' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#privilege-separation'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Privilege Separation&lt;/h3&gt;&lt;p&gt;It’s 1998 and the only serious language you have available to build in is C. You want to receive mail for a group of users, or authenticate and kick off a new SSH session. But those are complicated, multi-step operations, and nobody knows how to write secure C code; it’ll be 30 years before anyone figures that out. You assume you’re going to screw up a parse somewhere and cough up RCE. But you need privileges to get your job done.&lt;/p&gt;
&lt;p&gt;One solution: break the service up into smaller services. Give the services different user IDs. Connect services with group IDs. Mush the code around so that the gnarliest stuff winds up in the low-privileged services with the fewest connections to other services. Keep the stuff that needs to be privileged, like mailbox delivery or setting the login user, as tiny as you can.&lt;/p&gt;
&lt;p&gt;Call this approach &lt;a href="https://security.stackexchange.com/questions/115896/can-someone-explain-how-sshd-does-privilege-separation"&gt;“privsep”&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Despite &lt;a href="https://cr.yp.to/qmail/qmailsec-20071101.pdf"&gt;what its author said&lt;/a&gt; about his design, this approach works well. It’s not foolproof, but it has in fact a pretty good track record. The major downside is that it takes a lot of effort to implement; your application needs to be aware that you’re doing it.&lt;/p&gt;
&lt;h4 id='aside-pledge' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#aside-pledge'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Aside: Pledge&lt;/h4&gt;&lt;p&gt;If you can change your applications to fit the sandbox, you can take privsep pretty far. OpenBSD got this right with &lt;a href="http://www.openbsd.org/papers/hackfest2015-pledge/mgp00001.html"&gt;“pledge”&lt;/a&gt; and &lt;a href="https://www.openbsd.org/papers/BeckPledgeUnveilBSDCan2018.pdf"&gt;“unveil”,&lt;/a&gt; which allow programs to gradually ratchet down the access they get the kernel. It’s a better, more flexible idiom than seccomp, about which more later. But you’re not running OpenBSD, so, moving on.&lt;/p&gt;
&lt;h3 id='prelapsarian-containers' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#prelapsarian-containers'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Prelapsarian Containers&lt;/h3&gt;&lt;p&gt;People like to say “Docker isn’t a security boundary”, but that’s not so true anymore, though it once was.&lt;/p&gt;
&lt;p&gt;The core idea behind containers is kernel namespacing, which is chroot extended to other kernel identifiers — process IDs, user IDs, network interfaces. Configured carefully, these features give the appearance of a program running on its own machine, even as it shares a running kernel with other programs outside its container.&lt;/p&gt;
&lt;p&gt;But even with its own PID space, its own users and groups, and its own network interfaces, we still can’t have processes writing handler paths to &lt;code&gt;/sys&lt;/code&gt;, rebooting the system, loading kernel modules, and making new device nodes, and while many of these concerns can be avoided simply by not running as root, not all of them can.&lt;/p&gt;
&lt;p&gt;Systems security people spent almost a decade dunking on Docker because of all the gaps in this simplified container model. But nobody really runs containers like this anymore.&lt;/p&gt;
&lt;h3 id='incarceration' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#incarceration'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Incarceration&lt;/h3&gt;&lt;p&gt;Enter mandatory access control,  system call filtering, and capabilities.&lt;/p&gt;
&lt;p&gt;Mandatory access control frameworks (AppArmor is the one you’ll see) offer system- (or container-) wide access control lists. You can read a &lt;a href="https://github.com/moby/moby/blob/master/profiles/apparmor/template.go"&gt;version of Docker’s default AppArmor template&lt;/a&gt; to see what problems this fixes; it’s a nice concise description of the weaknesses of namespaces on their own.&lt;/p&gt;
&lt;p&gt;System call filters let us turn off kernel features; in 2020, if you’re filtering system calls, you’re probably doing it with seccomp-bpf.&lt;/p&gt;
&lt;p&gt;Capabilities split “root” into a whole mess of sub-privileges, ensuring that there’s rarely a need to give any program superuser access.&lt;/p&gt;
&lt;p&gt;There are lots of implementations of this idea.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/"&gt;Modern Docker&lt;/a&gt;, for instance, takes advantage of all these features. Though imperfect, the solution Docker security people arrived at is, I think, a success story. Developers don’t harden their application environments consciously, and yet, for the most part, they also don’t run containers privileged, or give them &lt;a href="https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities"&gt;extra capabilities&lt;/a&gt;, or disable the MAC policies and &lt;a href="https://github.com/moby/moby/blob/master/profiles/seccomp/default.json"&gt;system call filters&lt;/a&gt; Docker enforces by default.&lt;/p&gt;
&lt;p&gt;It may be even easier to jail a process outside of Docker; Googlers built &lt;a href="https://github.com/google/minijail"&gt;minijail&lt;/a&gt; and &lt;a href="https://github.com/google/nsjail"&gt;nsjail&lt;/a&gt;, Cloudflare has &lt;a href="https://github.com/cloudflare/sandbox"&gt;“sandbox”&lt;/a&gt;, there’s &lt;a href="https://firejail.wordpress.com/"&gt;“firejail”&lt;/a&gt;, which is somewhat tuned for things like browsers, and &lt;a href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter="&gt;systemd will do some of this work for you&lt;/a&gt;. Which tool is a matter of taste; nsjail has &lt;a href="https://github.com/google/kafel/"&gt;nice BPF UX&lt;/a&gt;; firejail interoperates with AppArmor. Some of them can be &lt;a href="https://github.com/cloudflare/sandbox/issues/1"&gt;preloaded&lt;/a&gt; into uncooperative processes.&lt;/p&gt;
&lt;p&gt;With namespaced jails, we’ve arrived at the most popular current endpoint for workload isolation. You can do better, but the attacks you’ll be dealing with start to get subtle.&lt;/p&gt;
&lt;h3 id='language-runtimes' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#language-runtimes'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Language Runtimes&lt;/h3&gt;&lt;p&gt;A limitation of jailed application environments is that they tend to be applied container- or at least process-wide. At high-volumes, allocating a process for every job might be expensive.&lt;/p&gt;
&lt;p&gt;If you relax the requirement to run ordinary Unix programs, you can get some of the benefits of jails without fine-grained per-process security models. Just compile everything to Javascript and run them in v8 isolates. The v8 language runtime makes promises, which you might or might not trust, about what cotenant jobs can access. Or you could use &lt;a href="https://github.com/bytecodealliance/lucet"&gt;Fastly’s Lucet&lt;/a&gt; &lt;a href="https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md"&gt;serverside WASM&lt;/a&gt; framework.&lt;/p&gt;
&lt;p&gt;From a security perspective, assuming you trust the language runtimes (I guess I do) these approaches are attractive when you can expose a limited system interface, which is what everyone does with them, and less attractive as a general design if you need all of POSIX.&lt;/p&gt;
&lt;h3 id='emulation' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#emulation'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Emulation&lt;/h3&gt;&lt;p&gt;Here’s a problem we haven’t addressed yet: you can design an intricate, minimal whitelist of system calls, drop all privileges, and cut most of the filesystem off. But then a Linux kernel developer restructures the memory access checks the kernel uses when deref’ing pointers passed to system calls, and someone forgets to tell the person who maintains waitid(2), and now &lt;a href="https://salls.github.io/Linux-Kernel-CVE-2017-5123/"&gt;userland programs can pass kernel addresses to waitid&lt;/a&gt; and whack random kernel memory. waitid(2) is innocuous, you weren’t going to filter it out, and yet there you were, boned.&lt;/p&gt;
&lt;p&gt;Or, how about this: every time a process faults an address, the kernel has to look up the backing storage to resolve the address. Since this is relatively slow, the kernel caches. But it has to keep those caches synchronized between all the threads in a process, so the per-thread caches get counters tied to the containing process. Except: the counters are 32 bits wide, and &lt;a href="https://googleprojectzero.blogspot.com/2018/09/a-cache-invalidation-bug-in-linux.html"&gt;the invalidation logic is screwed up&lt;/a&gt;, so that if you roll the counter, then immediately spawn a thread, then have that thread roll the counter again, you can desynchronize a thread’s cache and get the kernel to follow stale pointers.&lt;/p&gt;
&lt;p&gt;Bugs like this happen. They’re called kernel LPEs. A lot of them, you can mitigate by tightening system call and device filters, and compiling a minimal kernel (you weren’t really using IPv6 DCCP anways). But some of them, like Jann Horn’s cache invalidation bug, you can’t fix that way. How concerned you are about them depends on your workloads. If you’re just running your own applications, you might not care much: the attacker exploiting this flaw already has RCE on your systems and thus some access to your internal network. If you’re running someone else’s applications, you should probably care a lot, because this is your primary security barrier.&lt;/p&gt;
&lt;p&gt;If namespaces and filters constitute a “jail”, gVisor is The Village from The Prisoner. Instead of just filtering system calls, what if we just reimplement most of Linux? We run ordinary Unix programs, but intercept all the system calls, and, for the most part, instead of passing them to the kernel, we satisfy them ourselves. The Linux kernel has almost 400 system calls. How many of them do we need to efficiently emulate the rest? gVisor needs less than 20.&lt;/p&gt;
&lt;p&gt;With those, gVisor implements basically all of Linux in userland. Processes. Devices. Tasks. Address spaces and page tables. Filesystems. TCP/IP; the entire IP network stack, all reimplemented, in Go, backended by native Linux userland.&lt;/p&gt;
&lt;p&gt;The pitch here is straightforward: you’re unlikely to have routine exploitable memory corruption flaws in Go code. You are sort of likely to have them in the C-language Linux kernel. Go is fast enough to credibly emulate Linux in userland. Why expose C code if you don’t have to?&lt;/p&gt;
&lt;p&gt;As batshit as this plan is, it works surprisingly well; you can build gVisor and &lt;code&gt;runsc&lt;/code&gt;, its container runtime, relatively easily. Once you have &lt;code&gt;runsc&lt;/code&gt; installed, it will run Docker containers for you. After reading the code, I sort of couldn’t believe it was working as well as it did, or, if it was, that it was actually using the code I had read. But I scattered a bunch of panic calls across the codebase and, yup, that all that stuff is actually happening. It’s pretty amazing.&lt;/p&gt;
&lt;p&gt;You are probably strictly better off with gVisor than you are with a tuned Docker configuration, and I like it a lot. The big downside is performance; you’ll be looking at a low-double-digits percentage hit, degrading with I/O load. Google runs this stuff at scale in GCE; you can probably get away with it too. If you’re running gVisor, you should brag about it, because, again, gVisor is pretty bananas.&lt;/p&gt;
&lt;h3 id='lightweight-virtualization' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#lightweight-virtualization'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Lightweight Virtualization&lt;/h3&gt;&lt;p&gt;If you’re worried about kernel attack surface but don’t want to reimplement the entire kernel in userland, there’s an easier approach: just virtualize. Let Linux be Linux, and boot it in a virtual machine.&lt;/p&gt;
&lt;p&gt;You almost certainly already trust virtualization; if hypervisors are comprehensively broken, so is all of AWS, GCE, and Azure. And Linux &lt;a href="https://lwn.net/Articles/658511/"&gt;makes hypervising pretty simple&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;The challenge here is primarily about performance. A big part of the point of containers is that they’re lightweight. In a sense, the grail of serverside isolation is virtualization that’s light enough to run container workloads.&lt;/p&gt;
&lt;p&gt;It turns out, this is a reasonable ask. A major part of what makes virtual machines so expensive is hardware emulation, with enough fidelity to run multiple operating systems. But we don’t care about diverse operating systems; it’s usually fine to constrain our workloads to Linux. How lightweight can we a virtual machine if it’s only going to boot a simple Linux kernel, with simple devices?&lt;/p&gt;
&lt;p&gt;Turns out: pretty lightweight! So we’ve got Kata Containers, which is the big-company supported serverside lightweight virtualization project that came out of Intel’s Clear Containers (mission statement: “come up with a container scheme that is locked in to VT-x”). Using QEMU-Lite, Kata gets rid of BIOS boot overhead, replaces real devices with their virtio equivalents, and aggressively caches, and manages to get boot time down by like 75%. &lt;a href="https://elinux.org/images/4/44/Przywara.pdf"&gt;kvmtool&lt;/a&gt;, an alternative KVM runtime, gets even lighter.&lt;/p&gt;
&lt;p&gt;There’s two catches.&lt;/p&gt;
&lt;p&gt;The first, and really the big problem for the whole virtualization approach, is that you need bare metal servers to efficiently do lightweight virtualization; you want KVM but without nested virtualization. You’re probably not going to shell out for EC2 metal instances just to get some extra isolation.&lt;/p&gt;
&lt;p&gt;The second, more philosophical problem is that QEMU and kvmtool are &lt;a href="https://www.openwall.com/lists/oss-security/2019/09/17/1"&gt;relatively complicated C codebases&lt;/a&gt;, and we’d like to minimize our dependence on these. You could reasonably take the argument either way between gVisor, which emulates Linux in a memory-safe language, or Kata/kvmtool, which runs virtualized Linux with a small memory-unsafe hypervisor. They’re both probably better than locked-down &lt;code&gt;runc&lt;/code&gt; Docker, though.&lt;/p&gt;
&lt;h3 id='firecracker' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#firecracker'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Firecracker&lt;/h3&gt;&lt;p&gt;Lightweight virtualization is how AWS runs Lambda, its function-as-a-service platform, and Fargate, its serverless container platform. But rather than trusting (and painstaking tuning) QEMU, AWS reimplemented it, in Rust. The result is Firecracker.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.usenix.org/system/files/nsdi20-paper-agache.pdf"&gt;Firecracker&lt;/a&gt; is a VMM optimized for security. It’s really kind of difficult to oversell how clean Firecracker is; the Firecracker paper boasts that they’ve implemented their block device in around 1400 lines of Rust, but it looks to me like they’re counting a lot of test code; you only need to get your head around a couple hundred lines of Rust code to grok it. The network driver, which adapts a Linux tap device to a virtio device a guest Linux kernel can talk to, is about 700 lines before you hit tests — and that’s rust, so something like 1/3 of those lines are use-statements! It’s really great.&lt;/p&gt;
&lt;p&gt;The reason Firecracker (and, if you overlook the C code, kvmtool) can be this simple is that they’re pushing the system complexity down a layer. It’s still there; you’re booting an actual, make-menuconfig’d kernel, in all of it’s memory-unsafe glory. But you’re doing it inside a hypervisor where, in the Firecracker case, really you’re only worried about the integrity of the kvm subsystem itself.&lt;/p&gt;
&lt;p&gt;We aren’t yet significant contributors to Firecracker, but it still feels weird talking the project up because it’s such a core part of our offering. That said: the team at AWS really did this thing the Western District Way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Firecracker VMM is tiny, easily readable, and deliberately implements the minimal number of concepts required to run a Linux server workload. 
&lt;/li&gt;&lt;li&gt;The VMM is written in Rust.
&lt;/li&gt;&lt;li&gt;The VMM seccomp-bpf’s itself down to something like &lt;a href="https://github.com/firecracker-microvm/firecracker/blob/master/src/vmm/src/default_syscalls/filters.rs"&gt;40 system calls&lt;/a&gt;], several, including basic things like &lt;code&gt;fcntl&lt;/code&gt;, &lt;code&gt;socket&lt;/code&gt;, and &lt;code&gt;ioctl&lt;/code&gt;, with tight argument filters.
&lt;/li&gt;&lt;li&gt;Runs itself under an &lt;a href="https://github.com/firecracker-microvm/firecracker/blob/master/docs/jailer.md"&gt;external jailer&lt;/a&gt; that chroots, namespaces, and drops privileges. 
&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id='general-thoughts' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#general-thoughts'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;General Thoughts&lt;/h3&gt;&lt;p&gt;Keep in mind, I think, that no matter how intricate your Linux system isolation is, the most important attack surface you need to reduce is exposure to your network. If you can spend time segmenting an unsegmented single-VPC network or further tightening the default Docker seccomp-bpf policy, your time is probably better spent on the network.&lt;/p&gt;
&lt;p&gt;Remember also that when security tools designers think about isolation and attack surface reduction, they’re generally assuming that you need ordinary tools to run, and ordinary tools want Internet access; your isolation tools aren’t going to do the network isolation out of the box, the way they might, for instance, shield you from Video4Linux bugs.&lt;/p&gt;
&lt;p&gt;It seems to me like, for new designs, the basic menu of mainstream options today is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jailing otherwise-unmanaged Unix programs with &lt;code&gt;nsjail&lt;/code&gt; or something like it.
&lt;/li&gt;&lt;li&gt;Running unprivileged Docker containers, perhaps with a tighter seccomp profile than the default.
&lt;/li&gt;&lt;li&gt;Going full gVisor. 
&lt;/li&gt;&lt;li&gt;Running Firecracker, either directly or, in a K8s environment, with something like Kata. 
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;These are all valid options! I’ll say this: for ROI purposes, if time and effort is a factor, and if I wasn’t hosting hostile code, I would probably tune an &lt;code&gt;nsjail&lt;/code&gt; configuration before I bought into a containerization strategy.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Serve small with Fly.io and GoStatic</title>
    <link rel="alternate" href="https://fly.io/blog/serve-small-with-fly-io-and-gostatic/"/>
    <id>https://fly.io/blog/serve-small-with-fly-io-and-gostatic/</id>
    <published>2020-07-27T01:00:00+01:00</published>
    <updated>2020-07-27T14:12:27+01:00</updated>
    <author>
      <name>Dj Walker-Morgan</name>
    </author>
    <content type="html">&lt;p&gt;Static websites are great for carrying unchanging content, be it assets, images, fonts or even, as in this case, an entire site. Well, I say entire site, but if you saw my &lt;a href="/blog/hugo-s-there-flying-with-hugo-and-caddy/"&gt;last article&lt;/a&gt;, you&amp;#39;ll know I recently rebranded a Maker organization and needed to deploy a &amp;quot;signpost&amp;quot; page pointing people to the new site. I want this signpost to have a tiny footprint so it will never cost anything to deploy.&lt;/p&gt;
&lt;p&gt;Now, the thing with Docker images is that you don&amp;#39;t really notice the layers of OS and applications that pile up in the background. Something as notionally simple as say running Apache HTTPD will still need an OS layer under it, no matter how minimal, you put the two parts together and the image size soon builds up. And you still have to add the content.&lt;/p&gt;
&lt;p&gt;This is where something like &lt;a href="https://github.com/PierreZ/goStatic"&gt;GoStatic&lt;/a&gt; comes in. It&amp;#39;s a small, self-contained web page server which can run in a bare Docker image - no OS, just the binary. As the author points out, the official Golang images can weigh in with as much as half a gigabyte of image. For GoStatic, the image is an unchunky 6MB.&lt;/p&gt;
&lt;h2 id='go-gostatic' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#go-gostatic'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Go GoStatic&lt;/h2&gt;&lt;p&gt;So, how do you make use of GoStatic on Fly? Let&amp;#39;s step though it now.&lt;/p&gt;
&lt;p&gt;One thing you need to know is that by default GoStatic uses port 8043. So add &lt;code&gt;-p 8043&lt;/code&gt; to your &lt;code&gt;fly init&lt;/code&gt; command when you create your project. That&amp;#39;ll route traffic to port 80 and 443 to port 8043 on the application.&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight '&gt;&lt;code&gt;❯ mkdir examplegostatic
❯ cd examplegostatic
❯ fly init examplegostatic -p 8043
Selected App Name: examplegostatic

? Select organization: Dj (dj)

? Select builder: Dockerfile
(Create an example Dockerfile)

New app created
Name = examplegostatic
Owner = dj
Version = 0
Status =
Hostname = &amp;lt;empty&amp;gt;

Wrote config file fly.toml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We already have an index.html we want to serve, so our next stop is the Dockerfile. Delete the example contents and replace it with just two lines.&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight docker'&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; pierrezemb/gostatic&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.html /srv/http/index.html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we are ready to deploy! Just run &lt;code&gt;flyctl deploy&lt;/code&gt;:&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight '&gt;&lt;code&gt;Deploying examplegostatic
==&amp;gt; Validating App Configuration
--&amp;gt; Validating App Configuration done
Services
TCP 80/443 ⇢ 8043

Deploy source directory '/Users/dj/examplegostatic'
Docker daemon available, performing local build...
==&amp;gt; Building with Dockerfile
Using Dockerfile: /Users/dj/examplegostatic/Dockerfile
Step 1/2 : FROM pierrezemb/gostatic
 ---&amp;gt; 4569615e9ed0
Step 2/2 : COPY index.html /srv/http/index.html
 ---&amp;gt; b0b723d0cb24
Successfully built b0b723d0cb24
Successfully tagged registry.fly.io/examplegostatic:deployment-1595848012
--&amp;gt; Building with Dockerfile done
Image: registry.fly.io/examplegostatic:deployment-1595848012
Image size: 7.7 MB
==&amp;gt; Pushing Image
The push refers to repository [registry.fly.io/examplegostatic]
77bf40a52322: Pushed
3530b7ebed24: Pushed
deployment-1595848012: digest: sha256:d60567799841a4480e410acef113d33c1156eb960b94ae591931801089f61b1a size: 735
--&amp;gt; Done Pushing Image
==&amp;gt; Optimizing Image
--&amp;gt; Done Optimizing Image
==&amp;gt; Creating Release
Release v0 created
Deploying to : examplegostatic.fly.dev

Monitoring Deployment
You can detach the terminal anytime without stopping the deployment

1 desired, 1 placed, 1 healthy, 0 unhealthy [health checks: 1 total, 1 passing]
--&amp;gt; v0 deployed successfully

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once deployed, all you need to do then is &lt;code&gt;flyctl open&lt;/code&gt; and a browser will open and navigate to the site.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blog/2020-07-27/screenshot.png" alt="Our served page" /&gt;&lt;/p&gt;
&lt;p&gt;You&amp;#39;ll also notice that this has been upgraded to an https connection. All that is left is to attach a custom domain to it and we&amp;#39;re done.&lt;/p&gt;
&lt;h2 id='behind-the-scenes' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#behind-the-scenes'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Behind the scenes&lt;/h2&gt;&lt;p&gt;So, what magic is going on here? Well, the &lt;a href="https://github.com/PierreZ/goStatic/blob/master/Dockerfile"&gt;Dockerfile&lt;/a&gt; in GoStatic explains a lot of it.&lt;/p&gt;
&lt;p&gt;It uses a Docker multistage build to build our server binary in the first stage. Then it starts a new stage from scratch, literally using the command &lt;code&gt;FROM SCRATCH&lt;/code&gt;. This says that there is no base image, just start building on top of nothing, an empty image. The rest of the GoStatic Dockerfile creates a passwd file so there are some usernames to work with and copies over the GoStatic binary.&lt;/p&gt;
&lt;p&gt;And then it all hands over to our own Dockerfile. GoStatic serves files out of &lt;code&gt;/srv/http&lt;/code&gt; so we copy over our &lt;code&gt;index.html&lt;/code&gt; to that directory. And that&amp;#39;s it. Everything else is managed by Fly, the build, the deployment and the upgrading to an https connection. There used to be a version of GoStatic which would handle HTTPS connections and certificates, but that functionality has been retired now servers like Caddy exist. On Fly, the lack of HTTPS support means it&amp;#39;s simple to just let Fly take on the HTTPS work for you.&lt;/p&gt;
&lt;h2 id='a-small-squeeze' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#a-small-squeeze'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;A Small Squeeze&lt;/h2&gt;&lt;p&gt;One last tip. Fly deploys new applications with 512MB of RAM and about a quarter of a virtual CPU. It&amp;#39;s called a &lt;code&gt;micro-2x&lt;/code&gt; firecracker VM. But, we&amp;#39;re doing so little here, we could scale the VM size down. Let&amp;#39;s look at the scale settings:&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight '&gt;&lt;code&gt;~/examplegostatic
❯ flyctl scale show
     Scale Mode: Standard
      Min Count: 0
      Max Count: 10
        VM Size: micro-2x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And now we can set the vm size:&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight '&gt;&lt;code&gt;~/examplegostatic
❯ flyctl scale vm micro-1x
Scaled VM size to micro-1x
      CPU Cores: 0.12
         Memory: 128 MB
  Price (Month): $2.670000
 Price (Second): $0.000001
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you want to find out about the other VM sizes, run &lt;code&gt;flyctl platform vm-sizes&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id='wrapping-up' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#wrapping-up'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Wrapping up&lt;/h2&gt;&lt;p&gt;We&amp;#39;ve got ourselves a tiny static web server and deployed it to &lt;a href="http://fly.io"&gt;Fly.io&lt;/a&gt;, and as a bonus, shrunk the VM&amp;#39;s footprint to match it and save running costs. Enjoy!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Hugo's There - Flying with Hugo and Caddy</title>
    <link rel="alternate" href="https://fly.io/blog/hugo-s-there-flying-with-hugo-and-caddy/"/>
    <id>https://fly.io/blog/hugo-s-there-flying-with-hugo-and-caddy/</id>
    <published>2020-07-21T01:00:00+01:00</published>
    <updated>2020-07-27T13:20:15+01:00</updated>
    <author>
      <name>Dj Walker-Morgan</name>
    </author>
    <content type="html">&lt;p&gt;There I was wondering what to do about a website for a new community venture I was running where I thought, yes, let&amp;#39;s generate the site with &lt;a href="https://gohugo.com"&gt;Hugo&lt;/a&gt;, serve it with &lt;a href="https://caddyserver.com/"&gt;Caddy&lt;/a&gt; and run it all on Fly. Why Hugo and Caddy? Well, they both have good reputations as Go-based tooling thats compact and powerful, so let&amp;#39;s go make and host a site...&lt;/p&gt;
&lt;h2 id='fly-first' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#fly-first'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Fly First&lt;/h2&gt;&lt;p&gt;I&amp;#39;ll need to be able to reference what the app name, and consequently site name, will be. Because of that, I&amp;#39;m going to start by initializing my Fly deployment:&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight '&gt;&lt;code&gt;$ flyctl init makeronicc --dockerfile -p 80

Selected App Name: makeronicc

? Select organization: Dj (dj)

New app created
  Name     = makeronicc
  Owner    = dj
  Version  = 0
  Status   =
  Hostname = &amp;lt;empty&amp;gt;

Wrote config file fly.toml

$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Select your own app name or let the system generate one for you; it won&amp;#39;t matter as we&amp;#39;re going to front this set-up to a custom domain. We&amp;#39;re going to be using a Dockerfile (hence &lt;code&gt;--dockerfile&lt;/code&gt;) and our server will operate on port 80 (&lt;code&gt;-p 80&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id='the-hugo-configuration' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#the-hugo-configuration'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;The Hugo Configuration&lt;/h2&gt;&lt;p&gt;The site doesn&amp;#39;t have much content and a stroll through the &lt;a href="https://gohugo.io/getting-started/quick-start/"&gt;Hugo Quickstart&lt;/a&gt; will get us a front page and a blog article built in no time at all. It boils down to installing hugo, then installing a hugo theme and finally creating a &lt;code&gt;config.toml&lt;/code&gt; file. Here&amp;#39;s ours:&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight toml'&gt;&lt;code&gt;&lt;span class="py"&gt;baseURL&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://makeronicc.fly.dev"&lt;/span&gt;
&lt;span class="py"&gt;languageCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"en-us"&lt;/span&gt;
&lt;span class="py"&gt;title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Makeroni"&lt;/span&gt;
&lt;span class="py"&gt;theme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ananke"&lt;/span&gt;
&lt;span class="nn"&gt;[params]&lt;/span&gt;
&lt;span class="py"&gt;site_logo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/images/makeroni-logo-small.png"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;#39;s enough for our basic site to build running &lt;code&gt;hugo&lt;/code&gt; which generates all the static files. Running &lt;code&gt;hugo server -D&lt;/code&gt; lets us browse it locally on localhost:1313. The one thing to note? We&amp;#39;ll be serving this site up on the &lt;a href="http://makeronicc.fly.dev"&gt;makeronicc.fly.dev&lt;/a&gt; domain for now.&lt;/p&gt;
&lt;h2 id='docker-and-hugo' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#docker-and-hugo'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Docker And Hugo&lt;/h2&gt;&lt;p&gt;Next we want to get Docker to do the build work. Time to make a Dockerfile.&lt;/p&gt;
&lt;p&gt;There are various Hugo docker images out there but the one I like is &lt;a href="https://hub.docker.com/r/klakegg/hugo/"&gt;&lt;code&gt;klakegg/hugo&lt;/code&gt;&lt;/a&gt; on Docker Hub. As well as having Docker images that can be used for running Hugo as a Docker container, there&amp;#39;s an ONBUILD image. This is designed to be a stage in a multi-stage Docker build. It&amp;#39;s run as part of the pipeline, but then results can be copied from its image to a new, cleaner image, less all the build tools.&lt;/p&gt;
&lt;p&gt;So our Dockerfile starts like this:&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight docker'&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; klakegg/hugo:0.74.0-onbuild AS hugo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That, when built with Docker, will load up the image and the current working directory contents, run &lt;code&gt;hugo&lt;/code&gt; over it and deposit the results in &lt;code&gt;/target&lt;/code&gt; in the hugo image. Hugo build, done.  Now, let&amp;#39;s talk about serving it up.&lt;/p&gt;
&lt;h2 id='caddy-hack' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#caddy-hack'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Caddy Hack&lt;/h2&gt;&lt;p&gt;Now we come to Caddy, which is a great web server &amp;quot;built for now&amp;quot; - It has integrated handling of obtaining and managing Let&amp;#39;s Encrypt certificates so running an HTTPS site becomes super-simple. There&amp;#39;s only one issue - Fly already does all that certificate management for us, so although we want Caddy because it&amp;#39;s compact and easy to work with, we&amp;#39;re going to want to turn off Caddy&amp;#39;s own certificate system.&lt;/p&gt;
&lt;p&gt;Caddy is configured in a number of ways, JSON, API or the Caddyfile. I use the Caddyfile for this as its more human-readable. But now a public service announcement:&lt;/p&gt;
&lt;div class="callout"&gt;
When you search for Caddy and, well, anything at all, when you get to a result, scroll to the top of the page to make sure you aren&amp;#39;t on the Caddy 1 documentation. Caddy 2 is the current version but the google-juice for Caddy 1 documentation is still super high and the two are so similar yet different, it can be terribly frustrating to keep landing on the wrong docs.&lt;/div&gt;&lt;p&gt;Right, back to creating our Caddyfile. Most of what I just talked about can be summed up in one opening block.&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight caddy'&gt;&lt;code&gt;{
    auto_https off
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That turns off all the certificate management. Now we can tell Caddy to serve files for our &lt;a href="http://makeroni.cc"&gt;makeroni.cc&lt;/a&gt; domain.&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight caddy'&gt;&lt;code&gt;http://makeronicc.fly.dev {
    root * /usr/share/caddy
    file_server
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice that this is just for the http protocol connections. That&amp;#39;s because, once the TLS connection has passed through the Fly edge, it travels on the encrypted Fly network as a normal HTTP request.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s the Caddyfile created. Now to pull the two parts together in the Dockerfile.&lt;/p&gt;
&lt;h2 id='adding-caddy' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#adding-caddy'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Adding Caddy&lt;/h2&gt;&lt;p&gt;At the moment, our Dockerfile simply brings in and runs the Hugo static generator at build time. We need to take the results of that and put it into a Caddy docker image. There are &lt;a href="https://registry.hub.docker.com/_/caddy"&gt;official Caddy images&lt;/a&gt;, so I&amp;#39;ll use one of them:&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight docker'&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; klakegg/hugo:0.74.0-onbuild AS hugo&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; caddy:2.1.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Docker will now start with this Caddy image. Our Caddyfile says it will serve files out of &lt;code&gt;/usr/share/caddy&lt;/code&gt; so we&amp;#39;ll want to copy the files from our Hugo build over to there by adding:&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight docker'&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=hugo /target/ /usr/share/caddy/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;--from&lt;/code&gt; points to the named image we created at the start with &lt;code&gt;AS hugo&lt;/code&gt;. Now all we need is to put the Caddyfile in place.&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight docker'&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./Caddyfile /etc/caddy/Caddyfile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id='ready-to-fly' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#ready-to-fly'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Ready To Fly&lt;/h2&gt;&lt;p&gt;We&amp;#39;re ready to publish the site. Run &lt;code&gt;flyctl deploy&lt;/code&gt; and watch as the Hugo site is built, copied into a Caddy image, that image is then flattened and despatched to a Fly firecracker node. You don&amp;#39;t have to worry about that though, just run &lt;code&gt;flyctl open&lt;/code&gt; and your browser will open on your application.&lt;/p&gt;
&lt;p&gt;One thing worth noticing is that, although we only configured &lt;a href="http://makeroni.fly.dev"&gt;http://makeroni.fly.dev&lt;/a&gt;, it&amp;#39;s being automatically upgraded to https: to secure the connection.&lt;/p&gt;
&lt;p&gt;But we aren&amp;#39;t done yet. Remember we wanted the site to be provisioned on makeroni.cc.&lt;/p&gt;
&lt;h2 id='fly-domain' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#fly-domain'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Fly Domain&lt;/h2&gt;&lt;p&gt;The first step is to get the DNS system to point &lt;a href="http://makeroni.cc"&gt;makeroni.cc&lt;/a&gt; to the IP address of &lt;a href="http://makeroni.fly.dev"&gt;makeroni.fly.dev&lt;/a&gt;. I can get the IP Address by running &lt;code&gt;flyctl ips list&lt;/code&gt;.&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight plain'&gt;&lt;code&gt;$ flyctl ips list

TYPE ADDRESS                              CREATED AT
v4   77.83.141.28                         2020-07-07T21:26:36Z
v6   2a09:8280:1:9f04:7aa6:a706:a3d7:ccba 2020-07-07T21:26:36Z

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We want the V4 address. Now, I need to go to the registrar of the DNS entry, in my case NameCheap, and get to the DNS management pages, specifically the Advanced Management page of that.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s there I can add an A record. &lt;code&gt;A @ 77.83.141.28&lt;/code&gt; (The @ goes in the host column on Namecheap).&lt;/p&gt;
&lt;p&gt;Save that and let it propogate and then go to &lt;a href="http://makeroni.cc"&gt;http://makeroni.cc&lt;/a&gt; and you should see your site. If you follow any links though, you&amp;#39;ll notice you are back on &lt;a href="https://makeronicc.fly.dev"&gt;https://makeronicc.fly.dev&lt;/a&gt;. That&amp;#39;s because Hugo generated all the links with that address.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s easy enough to fix (we&amp;#39;ll get back to it in a moment), but there&amp;#39;s another more important thing to look at.&lt;/p&gt;
&lt;p&gt;If I try to go to &lt;a href="https://makeroni.cc"&gt;https://makeroni.cc&lt;/a&gt; and I get an error saying the connection is not secure. I haven&amp;#39;t created a TLS certificate for the domain.&lt;/p&gt;
&lt;p&gt;The quickest way to do this is to add an AAAA record in the same way I added an A record. The AAAA record in a DNS record should point to the IP V6 address for the host; it&amp;#39;s up in the &lt;code&gt;flyctl ips list&lt;/code&gt; output too. So I add &lt;code&gt;AAAA @ 2a09:8280:1:9f04:7aa6:a706:a3d7:ccba&lt;/code&gt; to the DNS. With that in place and propagated I can now go and request a certificate.&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight plain'&gt;&lt;code&gt;$ flyctl certs create makeroni.cc
Hostname = makeroni.cc
Configured = true
Issued =
Certificate Authority = lets_encrypt
DNS Provider = enom
DNS Validation Instructions = CNAME _acme-challenge.makeroni.cc =&amp;gt; makeroni.cc.2yz0.flydns.net.
DNS Validation Hostname = _acme-challenge.makeroni.cc
DNS Validation Target = makeroni.cc.2yz0.flydns.net
Source = fly
Created At = just now
Status = Ready
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the traffic could flow securely.... Except there&amp;#39;s one last change we need to make to the Caddyfile.&lt;/p&gt;
&lt;h2 id='caddy-changes' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#caddy-changes'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Caddy Changes&lt;/h2&gt;&lt;p&gt;Remember I set up the Caddyfile with&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight caddy'&gt;&lt;code&gt;http://makeronicc.fly.dev {
    root * /usr/share/caddy
    file_server
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Well, I&amp;#39;m not serving the files on that URL now so I&amp;#39;ll need to change that to match our custom domain:&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight '&gt;&lt;code&gt;http://makeroni.cc/ {
    root * /usr/share/caddy
    file_server
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now it&amp;#39;ll respond to requests for files from the &lt;a href="http://makeroni.cc"&gt;makeroni.cc&lt;/a&gt; domain... but what if I want to make sure that people who accidentally access the old &lt;a href="http://makeronicc.fly.dev"&gt;makeronicc.fly.dev&lt;/a&gt; site end up in the right place. For that, another rule in the Caddyfile is needed:&lt;/p&gt;
&lt;div class='highlight relative active:show:child '&gt;&lt;button class='child absolute right:0 top:0 w:4 h:4 p:9p text:darkest-silver active:text:white transition z:1' data-copy-target='sibling' data-copy-success="%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' stroke-linecap='round' stroke-width='1.5' stroke='white' fill='none' stroke='currentColor'%3E%3Cpath d='M17.257 8.502l-7.43 6.996-3.083-3.083' /%3E%3Cpath d='M21.995 3.778c0-1.706-1.347-3.091-3.006-3.091H4.96c-1.659 0-3.006 1.385-3.006 3.091v14.424c0 1.706 1.347 3.091 3.006 3.091h14.029c1.659 0 3.006-1.385 3.006-3.091V3.778z' transform='matrix(.99798 0 0 .97057 .05 1.333)' /%3E%3C/svg%3E"&gt;&lt;svg class='pointer:off' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5'&gt;&lt;path d='M17.555 9.556a3.112 3.112 0 00-3.11-3.111H5.111A3.113 3.113 0 002 9.556v9.332A3.113 3.113 0 005.11 22h9.334a3.112 3.112 0 003.11-3.112V9.556z' /&gt;&lt;path d='M18.89 17.555A3.112 3.112 0 0022 14.444V5.112A3.112 3.112 0 0018.89 2H9.556a3.113 3.113 0 00-3.112 3.112' /&gt;&lt;/svg&gt;&lt;/button&gt;&lt;pre class='highlight '&gt;&lt;code&gt;http://makeronicc.fly.dev {
    redir https://makeroni.cc/
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now those old requests will head to our new server. Redeploy the image to Fly and everything is ready to roll.&lt;/p&gt;
&lt;h2 id='wrapping-up' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#wrapping-up'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Wrapping Up&lt;/h2&gt;&lt;p&gt;We&amp;#39;ve gone through configuring a multistage image build which generated a static Hugo site, then loaded it into an image with Caddy. We&amp;#39;ve configured Caddy for development deployments on Fly and then we&amp;#39;ve got ourselves a custom domain set up, and made that work with TLS certificates from Let&amp;#39;s Encrypt and a small modification to Caddy&amp;#39;s setup.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Flyctl Evolved - Fly Changelog</title>
    <link rel="alternate" href="https://fly.io/blog/flyctl-evolved-fly-changelog/"/>
    <id>https://fly.io/blog/flyctl-evolved-fly-changelog/</id>
    <published>2020-07-10T01:00:00+01:00</published>
    <updated>2020-07-10T15:55:12+01:00</updated>
    <author>
      <name>Dj Walker-Morgan</name>
    </author>
    <content type="html">&lt;p class="lead"&gt;
There&amp;#39;s a new flyctl (v0.0.137) available for your command line, with cleaner commands and extra helpers. Find out more about it in the Changelog.&lt;/p&gt;&lt;p&gt;This flyctl release brings in some big changes in the command structure as we move to an app-centric command style. What does that mean? Well, the &lt;code&gt;apps&lt;/code&gt; subcommand is being deprecated; we&amp;#39;ve kept it in place for this release but now all its commands have top level commands of their own:&lt;/p&gt;
&lt;div class='max-w:full overflow-x:auto bg:white shadow:lg r mb:4'&gt;&lt;table class='table:stripe table:stretch table:pad text:sm text:no-wrap m:0'&gt;&lt;thead&gt;&lt;tr&gt;
&lt;th&gt;Was&lt;/th&gt;
&lt;th&gt;Now&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;apps create&lt;/td&gt;
&lt;td&gt;init&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;apps destroy&lt;/td&gt;
&lt;td&gt;destroy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;apps list&lt;/td&gt;
&lt;td&gt;list apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;apps move&lt;/td&gt;
&lt;td&gt;move&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;apps restart&lt;/td&gt;
&lt;td&gt;restart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;apps resume&lt;/td&gt;
&lt;td&gt;resume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;apps suspend&lt;/td&gt;
&lt;td&gt;suspend&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;move&lt;/code&gt;, &lt;code&gt;restart&lt;/code&gt;, &lt;code&gt;resume&lt;/code&gt;, &lt;code&gt;suspend&lt;/code&gt; commands also now take an appname as their last argument. The status command has also followed suit in this change, so you can now type &lt;code&gt;flyctl status appname&lt;/code&gt; rather than &lt;code&gt;flyctl status -a appname&lt;/code&gt; - The &lt;code&gt;-a&lt;/code&gt; option will remain supported.&lt;/p&gt;
&lt;p&gt;The list command at the top level has been around for a while and has advantages over the older list command: you can match appnames with fragments of text and filter on status or organization. Talking about organizations, &lt;code&gt;flyctl list orgs&lt;/code&gt; will list the organizations your account has access to.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ve also made some small usability changes in how you initialize an application. The &lt;code&gt;init&lt;/code&gt; command offers you a selection of builders or the chance to use a Dockerfile. If you don&amp;#39;t have one, &lt;code&gt;init&lt;/code&gt; will create one for you with a simple hello world deployment to get you going. If you don&amp;#39;t want the example generated, use &lt;code&gt;--dockerfile&lt;/code&gt; when running &lt;code&gt;init&lt;/code&gt;. And, yes, you can still specify a builder with &lt;code&gt;--builder&lt;/code&gt;, that&amp;#39;s not going away.&lt;/p&gt;
&lt;h2 id='other-changes' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#other-changes'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Other changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;move&lt;/code&gt; command will now tell you what organization your app is in so you know where you are moving it from.
&lt;/li&gt;&lt;li&gt;Setting and unsetting &lt;code&gt;secrets&lt;/code&gt; on suspended deployments is now blocked.
&lt;/li&gt;&lt;li&gt;Setting and unsetting &lt;code&gt;secrets&lt;/code&gt; will start a deployment monitor. Add the &lt;code&gt;--detach&lt;/code&gt; flag to return before starting the deployment monitor. 
&lt;/li&gt;&lt;li&gt;If you are setting a secret on an undeployed app, then we don&amp;#39;t start the deployment monitor, so no need to --detach.
&lt;/li&gt;&lt;li&gt;The &lt;code&gt;info&lt;/code&gt; command now supports &lt;code&gt;--host&lt;/code&gt; which will display just the host name, along with (&lt;code&gt;-n&lt;/code&gt; / &lt;code&gt;--name&lt;/code&gt; ) which just displays the appname. These flags are designed to make it easier to script with flyctl.
&lt;/li&gt;&lt;li&gt;The &lt;code&gt;version&lt;/code&gt; command now outputs just the bare version number - add &lt;code&gt;--full&lt;/code&gt; to get the detailed version/commit/date information if you need it.
&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id='platform-changes' class='relative flex ai:baseline mt:5 mb:3 active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pr:8p' aria-hidden='' href='#platform-changes'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;Platform changes&lt;/h2&gt;&lt;p&gt;It&amp;#39;s not all been flyctl changes. There&amp;#39;s a fix for a problem with cookie headers and HTTP/2 which is now in place. Also, if your application parses headers, you&amp;#39;ll find that a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Via"&gt;Via header&lt;/a&gt; has been added to enable applications to trace their route through the Fly edge.&lt;/p&gt;
&lt;p class="callout"&gt;
This is the Fly Changelog where we list all significant changes to the Fly platform, tooling and web sites. You can also use the RSS feed of just changelog posts available on &lt;a href="https://fly.io/changelog.xml"&gt;fly.io/changelog.xml&lt;/a&gt; or consult our dedicated &lt;a href="https://fly.io/changelog/"&gt;ChangeLog&lt;/a&gt; page with all the recent updates.&lt;/p&gt;&lt;!-- start --&gt;
&lt;h2 id='9th-july' class='relative flex ai:baseline mt:5 mb:3 pb:8p bb active:show:child text:dark-gray text:pre-wrap'&gt;&lt;a class='child text:dark-silver h:full absolute right:full flex ai:center jc:start pb:8p pr:8p' aria-hidden='' href='#9th-july'&gt;&lt;svg class='w h:3/5' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round'&gt;&lt;g buffered-rendering='static'&gt;&lt;path vector-effect='non-scaling-stroke' d='M12.354 16.596l-3.183 3.182c-1.171 1.172-3.071 1.172-4.243 0l-.707-.707c-1.17-1.171-1.17-3.071.001-4.241l4.597-4.597c1.172-1.172 3.071-1.172 4.243 0l.707.708M11.647 7.403l3.182-3.181c1.172-1.171 3.07-1.171 4.242 0l.707.707c1.172 1.171 1.172 3.071 0 4.242l-4.596 4.596c-1.172 1.173-3.071 1.173-4.242 0' /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;9th July&lt;/h2&gt;&lt;p&gt;&lt;strong class='text:dark-gray'&gt;flyctl&lt;/strong&gt;: Version &lt;a href="https://github.com/superfly/flyctl/releases/tag/v0.0.137"&gt;0.0.137 released&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; New top level commands
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Apps subcommand deprecated
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; &lt;code&gt;--host&lt;/code&gt; added to info command
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; &lt;code&gt;version&lt;/code&gt; displays bare version number, &lt;code&gt;--full&lt;/code&gt; displays full details
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; &lt;code&gt;secrets&lt;/code&gt; setting and unsetting will follow deployment where appropriate
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; &lt;code&gt;secrets&lt;/code&gt; now supports &lt;code&gt;--detach&lt;/code&gt;
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; &lt;code&gt;move&lt;/code&gt; command now prompts with current organization
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; &lt;code&gt;init&lt;/code&gt; command now prompts with list of builders or option to create/use Dockerfile
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; &lt;code&gt;init&lt;/code&gt; command supports &lt;code&gt;--dockerfile&lt;/code&gt; flag to completely skip builder query
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;strong class='text:dark-gray'&gt;Fly Platform/Web&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li class='changelog-item bg:lightest-blue text:dark-blue anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M18.217 8.658h-7.652M13.435 15.353H5.783M15.348 11.527l2.87-2.87-2.87-2.869M8.652 18.223l-2.87-2.87 2.87-2.87' /&gt;&lt;path d='M23 12.006c0 6.073-4.926 11-11 11-6.076 0-11-4.927-11-11 0-6.074 4.924-11 11-11 6.074 0 11 4.926 11 11z' /&gt;&lt;/svg&gt; Fixed a bug related to incoming HTTP/2 request cookies sent as multiple headers. Incoming HTTP/2 connections can present multiple cookie headers. The headers were sent on as is when the Fly edge downcast the connection to HTTP/1  which is used within the Fly network. Some servers could not handle the multiple headers though. Now, the downcasting process concatenates the multiple cookie headers into a single cookie header. 
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Added the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Via"&gt;Via header&lt;/a&gt; to both http requests and responses
&lt;/li&gt;&lt;li class='changelog-item text:darker-green anchors:underline anchors:inherit relative text:md leading:sm list:no-style mx:0 mt:0 mb:10p py:1 pb:1 pl:5 pr:1 r' style='background: hsl(145, 75%, 93%);'&gt;&lt;svg width='16' height='16' class='absolute left:1 top:1 mt:5p opacity:50' viewBox='0 0 24 24' stroke-linecap='round' stroke='currentColor' stroke-width='2' fill='none'&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175M12 5.913v12.175' /&gt;&lt;path d='M23 12c0 6.076-4.927 11-11 11-6.077 0-11-4.924-11-11S5.923 1 12 1c6.073 0 11 4.924 11 11zM5.913 12h12.175' /&gt;&lt;/svg&gt; Boosted the performance of the &lt;code&gt;Optimizing Image&lt;/code&gt; phase of deployment by making better use of existing identical images.
&lt;/li&gt;&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Run Apollo Server Close to Your Users</title>
    <link rel="alternate" href="https://fly.io/blog/run-apollo-graphql-close-to-your-users/"/>
    <id>https://fly.io/blog/run-apollo-graphql-close-to-your-users/</id>
    <published>2020-07-01T01:00:00+01:00</published>
    <updated>2020-08-04T13:29:55+01:00</updated>
    <author>
      <name>Kurt Mackey</name>
    </author>
    <content type="html">&lt;p class="lead"&gt;
Fly.io can run API servers close to users. It&amp;#39;s kind of like a CDN for your GraphQL server. Here&amp;#39;s a guide to building an edge &lt;a href="https://fly.io/docs/app-guides/graphql-edge-caching-apollo/"&gt;GraphQL Server with Apollo&lt;/a&gt; and Redis.&lt;/p&gt;&lt;p&gt;I&amp;#39;m a newly minted GraphQL convert. We built Fly on top of GraphQL and the experience turned me into a shameless cheerleader. An API format with static typing? That&amp;#39;s my jam.&lt;/p&gt;
&lt;p&gt;(If you don&amp;#39;t care for JAMStack puns you can just go read our guide on &lt;a href="https://fly.io/docs/app-guides/graphql-edge-caching-apollo/"&gt;building an Edge GraphQL service with Apollo&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Speaking of jam, you&amp;#39;ve probably used application stacks that push content close to users. Hosting JavaScript and markup on a CDN can help make an app snappy.&lt;/p&gt;
&lt;p&gt;You can &lt;em&gt;also&lt;/em&gt; apply CDN like infrastructure to a GraphQL API to get a nice speed boost. All you need is a way to run API servers and an application cache close to users.&lt;/p&gt;
&lt;p&gt;Which is why we built a platform to run API servers close to users and paired it with a global Redis cache service. It&amp;#39;s a great place to run Apollo Server, for example, with its &lt;a href="https://www.apollographql.com/docs/apollo-server/performance/caching/"&gt;cache capabilities&lt;/a&gt; and first class Redis support.&lt;/p&gt;
&lt;p&gt;We built a demo GraphQL API based on the Open Library REST API. Read &lt;a href="/docs/app-guides/graphql-edge-caching-apollo/"&gt;the guide&lt;/a&gt; or check out &lt;a href="https://github.com/fly-examples/edge-apollo-cache/"&gt;the source code&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
</feed>
