<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Sam Gleske</title>
        <description>adventures in computing</description>
        <link>https://sam.gleske.net/blog/feed.xml</link>
        <atom:link href="https://sam.gleske.net/blog/feed.xml" rel="self" type="application/rss+xml" />
            <item>
                <title>Create a Nexus proxy for downloading GitHub releases</title>
                <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#what-to-proxy-from-github&quot; id=&quot;markdown-toc-what-to-proxy-from-github&quot;&gt;What to proxy from GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#routing-rules&quot; id=&quot;markdown-toc-routing-rules&quot;&gt;Routing rules&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#proxying-github-releases&quot; id=&quot;markdown-toc-proxying-github-releases&quot;&gt;Proxying GitHub releases&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-to-download&quot; id=&quot;markdown-toc-how-to-download&quot;&gt;How to download&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In large environments, it makes sense to cache artifacts locally in-network
where downloads occur frequently.  An example of this is a proxy for CICD
environments performing regular builds.&lt;/p&gt;

&lt;p&gt;This guide covers how to configure Sonatype Nexus for downloading releases from
GitHub.  In general, you should only cache items you expect to never change or
wouldn’t want to change.&lt;/p&gt;

&lt;h1 id=&quot;what-to-proxy-from-github&quot;&gt;What to proxy from GitHub&lt;/h1&gt;

&lt;p&gt;There’s three types of GitHub downloads which I think fit this description:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitHub releases&lt;/li&gt;
  &lt;li&gt;Source code archives downloaded by Git SHA1&lt;/li&gt;
  &lt;li&gt;Source code archives downloaded by Git tag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The least reliable of the three types are GitHub releases and Git tag because
open source projects can change these.  The intent of a proxy is to provide
stability in an environment so even if the upstream changes the artifact the
local cache would not change (nor would I want it to).&lt;/p&gt;

&lt;h1 id=&quot;routing-rules&quot;&gt;Routing rules&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://help.sonatype.com/repomanager3/nexus-repository-administration/repository-management/routing-rules&quot;&gt;Routing rules&lt;/a&gt; in Nexus restrict what content is allowed to be resolved
from a repository within Nexus.  Because GitHub has standard URLs for
downloading for all projects, you can use a regular experession in a routing
rule.&lt;/p&gt;

&lt;p&gt;Routing rule settings:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GitHubReleases&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Description&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Download GitHub Releases.  Download source code archives via
Git SHA1 or tag.&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mode&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Allow&lt;/code&gt; (meaning any requests which do not match regex are not
allowed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, &lt;strong&gt;Matchers&lt;/strong&gt; are regular expressions which restrict downloads.  You
only need two matchers.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/[^/]+/[^/]+/releases/download/[^/]+/.+

/[^/]+/[^/]+/archive/([0-9a-f]{40}|refs/tags/.+)\.(zip|tar\.gz)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first matcher covers downloading from GitHub releases.  The second matcher
covers downloading archives from Git SHA1 or Git tag; in both, zip or tar.gz
formats.&lt;/p&gt;

&lt;h1 id=&quot;proxying-github-releases&quot;&gt;Proxying GitHub releases&lt;/h1&gt;

&lt;p&gt;Nexus has a &lt;a href=&quot;https://help.sonatype.com/repomanager3/nexus-repository-administration/formats/raw-repositories&quot;&gt;raw repository type&lt;/a&gt; which can be configured to proxy URLs.  Here are
its settings.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Content Disposition&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Attachment&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Remote Storage&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://github.com&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Auto blocking enabled&lt;/strong&gt;: enable (my opinion; if there’s GitHub outages)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Maximum component age&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-1&lt;/code&gt; (for release artifacts)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Maximum metadata age&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;30&lt;/code&gt; (minutes; my opinion)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Strict Content Type Validation&lt;/strong&gt;: Unchecked i.e. not strict&lt;/li&gt;
  &lt;li&gt;Set the &lt;strong&gt;Routing Rule&lt;/strong&gt; to the rule created in the previous section.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Not found cache enabled&lt;/strong&gt;: enable it&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Not found cache TTL&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt; (minutes; my opinion)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;how-to-download&quot;&gt;How to download&lt;/h1&gt;

&lt;p&gt;If you did all of this on a local host Nexus then you would prefix the
repository URL with the GitHub URL replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github.com&lt;/code&gt; with your Nexus
repository.&lt;/p&gt;

&lt;p&gt;Instead of&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://github.com/samrocketman/yml-install-files/releases/download/v2.14/universal.tgz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You would download&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://localhost:8081/repository/github/samrocketman/yml-install-files/releases/download/v2.14/universal.tgz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
                <pubDate>Fri, 06 Oct 2023 00:00:00 -0400</pubDate>
                <link>https://sam.gleske.net/blog/engineering/2023/10/06/nexus-proxy-github-releases.html</link>
                <guid isPermaLink="true">https://sam.gleske.net/blog/engineering/2023/10/06/nexus-proxy-github-releases.html</guid>
            </item>
            <item>
                <title>Securing tmp space</title>
                <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#securing-tmp&quot; id=&quot;markdown-toc-securing-tmp&quot;&gt;Securing tmp&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#preparing-on-disk-tmp&quot; id=&quot;markdown-toc-preparing-on-disk-tmp&quot;&gt;Preparing on-disk tmp&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#adding-fstab-entries-for-boot&quot; id=&quot;markdown-toc-adding-fstab-entries-for-boot&quot;&gt;Adding fstab entries for boot&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#mount-options-explained&quot; id=&quot;markdown-toc-mount-options-explained&quot;&gt;Mount options explained&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use Linux as a Desktop and this is one thing (of many) I do to better secure
it for general internet browsing.&lt;/p&gt;

&lt;h1 id=&quot;securing-tmp&quot;&gt;Securing tmp&lt;/h1&gt;

&lt;p&gt;There’s three main temporary files paths in Linux which is standard.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; on disk temp space&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/tmp&lt;/code&gt; on disk temp space&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/shm&lt;/code&gt; in-memory temp space&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These should be secured so that programs cannot be executed from them.  This
prevents a wide array of attacks which assume tmp is capable of executing.
Also, since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/shm&lt;/code&gt; is in-memory it should be limited.  I like to limit it to
at least 1GB or smaller since not a lot of programs use it.  1GB is a safe limit
and it will only take up memory if files are written to it.&lt;/p&gt;

&lt;h1 id=&quot;preparing-on-disk-tmp&quot;&gt;Preparing on-disk tmp&lt;/h1&gt;

&lt;p&gt;I like to share &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/tmp&lt;/code&gt; with the same file system.  This limits
the combination of both spaces.  The following prepares an on-disk image meant
to be used as temporary file storage.  The following commands are executed as
root.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; /mnt/tmp /root/images

&lt;span class=&quot;c&quot;&gt;# create a 2GB file-based filesystem&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;dd &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/root/images/tmp2g &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/dev/zero &lt;span class=&quot;nv&quot;&gt;bs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1024M &lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2
mkfs.ext4 /root/images/tmp2g

&lt;span class=&quot;c&quot;&gt;# change permissions to match /tmp with sticky bit&lt;/span&gt;
mount &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; loop /root/images/tmp2g /mnt/tmp
&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;1777 /mnt/tmp
umount /mnt/tmp

&lt;span class=&quot;c&quot;&gt;# clean up&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;rmdir&lt;/span&gt; /mnt/tmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;adding-fstab-entries-for-boot&quot;&gt;Adding fstab entries for boot&lt;/h1&gt;

&lt;p&gt;With the new file system stored under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/root&lt;/code&gt; we’ll be able to mount &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; with
a file system limited to 2GB.  To finish securing temporary files you’ll want to
add the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/fstab&lt;/code&gt; entries.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/root/images/tmp2g /tmp ext4 loop,strictatime,noexec,nodev,nosuid 0 0
/tmp /var/tmp none bind 0 0
tmpfs /dev/shm tmpfs defaults,noexec,nodev,nosuid,seclabel,size=1G 0 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A bind mount was created between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/tmp&lt;/code&gt; so they share the same
space-limited filesystem.  Once you reboot, the temporary filesystems will all
be updated (you don’t need to reboot but this is the lazy approach).&lt;/p&gt;

&lt;h1 id=&quot;mount-options-explained&quot;&gt;Mount options explained&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop&lt;/code&gt; will set up a loopback interface.  This treats a file like a device
(e.g. USB stick).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strictatime&lt;/code&gt; It updates the access time each time a file or its cache is
accessed. This increases the disk writes.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;noexec&lt;/code&gt; does not allow executables to run (even if their execute bit is set).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nodev&lt;/code&gt; character or block devices are not allowed on the file system.
Examples include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/null&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/zero&lt;/code&gt;, etc. so devices with similar
behavior are not allowed in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nosuid&lt;/code&gt; will not honor set-user-ID and set-group-ID bits or file capabilities
when executing programs from this filesystem.  This may be redundant with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;noexec&lt;/code&gt; but in general it is a good practice to have this set with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;noexec&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defaults&lt;/code&gt; will use the default options: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rw&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suid&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auto&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nouser&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;seclabel&lt;/code&gt; indicates that the filesystem is using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xattrs&lt;/code&gt; for labels and that
it supports label changes by setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xattrs&lt;/code&gt;.  If you’re not using
SELinux, then this is not necessary.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size=1G&lt;/code&gt; will limit the size of the in-memory tmpfs to 1GB.&lt;/li&gt;
&lt;/ul&gt;
</description>
                <pubDate>Sat, 29 Apr 2023 00:00:00 -0400</pubDate>
                <link>https://sam.gleske.net/blog/engineering/2023/04/29/securing-tmp.html</link>
                <guid isPermaLink="true">https://sam.gleske.net/blog/engineering/2023/04/29/securing-tmp.html</guid>
            </item>
            <item>
                <title>Guide to production docker images</title>
                <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#what-makes-a-good-production-image&quot; id=&quot;markdown-toc-what-makes-a-good-production-image&quot;&gt;What makes a good production image&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#an-example-application&quot; id=&quot;markdown-toc-an-example-application&quot;&gt;An example application&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#diving-into-from-scratch&quot; id=&quot;markdown-toc-diving-into-from-scratch&quot;&gt;Diving into “from scratch”&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#creating-our-most-minimal-docker-container&quot; id=&quot;markdown-toc-creating-our-most-minimal-docker-container&quot;&gt;Creating our most minimal Docker container&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#arm-support&quot; id=&quot;markdown-toc-arm-support&quot;&gt;ARM support&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#full-docker-example-with-tls-ca-and-tzinfo&quot; id=&quot;markdown-toc-full-docker-example-with-tls-ca-and-tzinfo&quot;&gt;Full Docker example with TLS CA and tzinfo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#working-with-shared-libraries&quot; id=&quot;markdown-toc-working-with-shared-libraries&quot;&gt;Working with shared libraries&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#summary&quot; id=&quot;markdown-toc-summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;what-makes-a-good-production-image&quot;&gt;What makes a good production image&lt;/h1&gt;

&lt;p&gt;I would like to cover initial basics agnostic to Linux distrobution or
recommended base images.  The intent here is to start by defining what makes a
good Docker image for an application.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The Docker image should only have dependencies necessary for running the
application.  This provides a few benefits.
    &lt;ul&gt;
      &lt;li&gt;Fewer extra software means improved security.&lt;/li&gt;
      &lt;li&gt;A smaller Docker image which means your app or API starts faster in cloud
provisioning services because it is fast to download.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The app should log to stdout and stderr.  Not write logs to disk.  This is
important because Docker can handle log management but not if it is written to
disk.&lt;/li&gt;
  &lt;li&gt;The app should launch in the foreground and manage its own child processes
from there.  Docker does a good job at process management.  Let Docker manage
passing signals to your application.&lt;/li&gt;
  &lt;li&gt;Even if launched in the foreground, you’ll still want the docker image to have
a process dedicated to PID 1 functions.  If you’re not familiar with this
issue I recommend the original &lt;a href=&quot;https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/&quot;&gt;phusion blog post&lt;/a&gt; calling out the
problem.  My preference is to bake in &lt;a href=&quot;https://github.com/Yelp/dumb-init&quot;&gt;Yelp dumb-init&lt;/a&gt; for a couple
of reasons.  dumb-init is small and I don’t have to worry about the
orchestration service supporting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--init&lt;/code&gt; flags for Docker.
    &lt;ul&gt;
      &lt;li&gt;If not addressing this issue while in production, you’ll find that
occasionally your service will auto-scale.  The auto-scaling could be a
symptom due to being constrained with resources.  It won’t be immediately
clear without good monitoring and easy to mistake for traffic balancing.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;When your application exits, provide a meaningful exit code.  Zero for success
and non-zero for failure.  Depending on the complexity of your application it
might assist troubleshooting if you provide documentation around failure exit
codes.  The documentation could even include tips on what went wrong and how
to resolve it such as a run book for a support team.&lt;/li&gt;
  &lt;li&gt;Your application should run as a normal user and not root user inside of the
container.  This improves security of both your app and the host in which it
resides.&lt;/li&gt;
  &lt;li&gt;Your application should start the container with &lt;a href=&quot;https://docs.docker.com/engine/security/&quot;&gt;default Linux
capabilities&lt;/a&gt; provided by Docker.  If possible, try not to
add any extra capabilities which would reduce the security if your application
runtime.&lt;/li&gt;
  &lt;li&gt;If your app requires outbound network connectivity involving TLS, then it
should include standard TLS certificate authorities for whatever cloud or
platform you’re working from.&lt;/li&gt;
  &lt;li&gt;If your app requires timezone information then you should include Linux
timezone files.&lt;/li&gt;
  &lt;li&gt;Even if you’re using a minimal image, you’ll want to follow the &lt;a href=&quot;https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard&quot;&gt;Linux
filesystem hierarchy standard&lt;/a&gt; and provide all of the above
recommendations even if building from scratch.  I will provide an example at
the end of this article.&lt;/li&gt;
  &lt;li&gt;If you add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHELL [&quot;/bin/sh&quot;, &quot;-exc&quot; ]&lt;/code&gt; at the beginning of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;
you get better debug output from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bin/sh&lt;/code&gt; while Docker is building the image.
This helps narrow down issues to the exact command that fails when a Docker
build fails.  It also forces a multi-statement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN&lt;/code&gt; command to exit with an
error; without the need to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; between commands.
    &lt;ul&gt;
      &lt;li&gt;Alternately, if you prefer bash to be you Docker RUN shell you can set
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHELL [&quot;/bin/bash&quot;, &quot;-exo&quot;, &quot;pipefail&quot;, &quot;-c&quot; ]&lt;/code&gt;; see bash manual for &lt;a href=&quot;https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html&quot;&gt;the
set builtin&lt;/a&gt;.&lt;/li&gt;
      &lt;li&gt;If you only want to debug a single RUN command you can prefix commands with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set&lt;/code&gt; instead of changing all RUN shells.  For example, instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN \&lt;/code&gt;
within examples, you could remove SHELL and add a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN set -ex; \&lt;/code&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are other good practices in general for applications such as integrating
application performance monitoring (APM), unit testing with code coverage,
documentation, and data flow diagrams.  However, this article is mostly
highlighting Docker so I don’t plan to dive into these other topics here.  They
are still good to build in as observability will add to service quality.&lt;/p&gt;

&lt;h1 id=&quot;an-example-application&quot;&gt;An example application&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Flask is a framework based on the current/old standard for Python web
frameworks: WSGI.&lt;/li&gt;
  &lt;li&gt;FastAPI is based on Starlette, which uses the newer standard for asynchronous
web frameworks: ASGI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the purposes of this write-up I developed a simple flask application (a REST
API with only one endpoint from the flask REST API tutorial).  If you’re
starting an API from scratch you might want to consider FastAPI, instead.
However, for the purposes of this writeup the application or framework doesn’t
matter.  The real star is showing examples of production ready Docker images.&lt;/p&gt;

&lt;p&gt;You can go view the example at &lt;a href=&quot;https://github.com/samrocketman/docker-production-ready-flask&quot;&gt;docker-production-ready-flask&lt;/a&gt;.
It follows recommendations from Flask documentation on &lt;a href=&quot;https://flask.palletsprojects.com/en/2.2.x/deploying/&quot;&gt;how to deploy flask to
production&lt;/a&gt; paired with Docker best practices.  The project
README goes into more detail.&lt;/p&gt;

&lt;h1 id=&quot;diving-into-from-scratch&quot;&gt;Diving into “from scratch”&lt;/h1&gt;

&lt;p&gt;Ivan Velichko has a great writeup on, &lt;a href=&quot;https://iximiuz.com/en/posts/containers-distroless-images/&quot;&gt;why Google distroless instead of “from
scratch”&lt;/a&gt;?  The short version of the article is the
following.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM scratch&lt;/code&gt; images are bad (e.g. minimal go apps from scratch) because:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;They can’t run as non-root.&lt;/li&gt;
  &lt;li&gt;Timezone information is missing so you will have Go bugs encountered at
runtime.&lt;/li&gt;
  &lt;li&gt;Standard directories (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/tmp&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt;) are missing which cause
issues with native Go calls that rely on temporary files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s inspect and verify.  For the purposes of inspection I will pull in a
&lt;a href=&quot;https://github.com/robxu9/bash-static/&quot;&gt;statically compiled version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s our minimal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; for inspection.&lt;/p&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; alpine&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; https://github.com/robxu9/bash-static/releases/download/5.1.016-1.2.3/bash-linux-x86_64 /bash&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;755 /bash
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; scratch&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=0 /bash /&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now to build and look around.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker build &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; minimal &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Launch the container.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; minimal /bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Look around.  There’s no standard GNU utilities so we’ll be limited to shell
built-in functions.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bash-5.1# &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
bash dev etc proc sys

bash-5.1# &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;dev/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 
dev/console dev/core dev/fd dev/full dev/mqueue dev/null dev/ptmx dev/pts
dev/random dev/shm dev/stderr dev/stdin dev/stdout dev/tty dev/urandom dev/zero

bash-5.1# &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;etc/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
etc/hostname etc/hosts etc/mtab etc/resolv.conf

bash-5.1# &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$$&lt;/span&gt;
1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Some observations:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It is a minimal container.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; is our shell and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proc&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sys&lt;/code&gt; are kernel filesystems so no need to
inspect them since they are kernel API mounts.&lt;/li&gt;
  &lt;li&gt;Because the bash prompt has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt; it means we are running as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; user.&lt;/li&gt;
  &lt;li&gt;We don’t need to worry about standard devices or filesystems like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/shm&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/zero&lt;/code&gt;, etc.&lt;/li&gt;
  &lt;li&gt;We are missing temporary directories such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/tmp&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;We’re running as PID 1 (verified with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo $$&lt;/code&gt;).  Bash can’t handle PID 1
signals.  Any app you put into a from scratch image will also unlikely handle
signals unless the app is explicitly designed for it.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc&lt;/code&gt; has standard networking mounts provided by Docker but we don’t have
standard user files like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/passwd&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/group&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;creating-our-most-minimal-docker-container&quot;&gt;Creating our most minimal Docker container&lt;/h1&gt;

&lt;p&gt;Accounting for best practices (except for TLS certificates and tzinfo)
let’s create an example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;ARG&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; base=alpine&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ${base}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/bin/sh&quot;, &quot;-exc&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# Prerequisites&lt;/span&gt;
  apk add --no-cache build-base; \
  # Directory structure and permissions
  mkdir -p base/bin base/tmp base/var/tmp base/etc base/home/nonroot base/sbin base/root; \
  chmod 700 /root; \
  chown root:root /root; \
  chmod 1777 base/tmp base/var/tmp; \
  chown 65532:65532 base/home/nonroot; \
  chmod 750 base/home/nonroot; \
  # UID and GID
  echo &apos;root:x:0:&apos; &amp;gt; /base/etc/group; \
  echo &apos;nonroot:x:65532:&apos; &amp;gt;&amp;gt; /base/etc/group; \
  echo &apos;root:x:0:0:root:/root:/sbin/nologin&apos; &amp;gt; /base/etc/passwd; \
  echo &apos;nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin&apos; &amp;gt;&amp;gt; /base/etc/passwd; \
  # init binary for PID 1
  wget -O base/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_&quot;`uname -m`&quot;; \
  chmod 755 base/bin/dumb-init; \
  # nologin binary
  echo &apos;int main() { return 1; }&apos; &amp;gt; nologin.c; \
  gcc -Os -no-pie -static -std=gnu99 -s -Wall -Werror -o base/sbin/nologin nologin.c; \
  echo &quot;Minimal Container version $VERSION&quot; &amp;gt; /etc/issue


&lt;span class=&quot;c&quot;&gt;# Add our example program (bash)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Note: you don&apos;t need this for your own application.  In this case static bash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#   is the example application running in user context within a minimal image&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Comment out these lines and update CMD for your own app.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  wget &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; base/bin/bash https://github.com/robxu9/bash-static/releases/download/5.1.016-1.2.3/bash-linux-&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;uname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;755 base/bin/bash

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; scratch&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=0 /base/ /&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/bin/dumb-init&quot;, &quot;--&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; nonroot&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; HOME=/home/nonroot USER=nonroot&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /home/nonroot&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/bin/bash&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Build it.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker build &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; minimal &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s look around!&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; minimal
bash-5.1&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;

bash-5.1&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$$&lt;/span&gt;
7

bash-5.1&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;/proc/1/exe &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt;
dumb-init v1.2.5

bash-5.1&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;
/home/nonroot

bash-5.1&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IFS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;$&apos;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;$&apos;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt; line&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt; &amp;lt; /proc/self/environ
&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
&lt;span class=&quot;nv&quot;&gt;HOSTNAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;7cdf06c28dd1
&lt;span class=&quot;nv&quot;&gt;TERM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;xterm
&lt;span class=&quot;nv&quot;&gt;HOME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/home/nonroot
&lt;span class=&quot;nv&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;nonroot

bash-5.1&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; /sbin/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; /bin/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; /var/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; /t&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
/sbin/nologin /bin/bash /bin/dumb-init /var/tmp /tmp

bash-5.1&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
bin dev etc home proc root sbin sys tmp var

bash-5.1&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;hello &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /tmp/file

bash-5.1&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; /tmp/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
/tmp/file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Observations&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We’re running as a normal user because the bash prompt has a dollar sign &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$&lt;/code&gt;
(not root).&lt;/li&gt;
  &lt;li&gt;Our shell is no longer PID 1.&lt;/li&gt;
  &lt;li&gt;We used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/proc&lt;/code&gt; kernel file system to verify that PID 1 is the
&lt;a href=&quot;https://github.com/Yelp/dumb-init&quot;&gt;dumb-init&lt;/a&gt; process.  If you’re curious about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/proc&lt;/code&gt; you can read
about it within the &lt;a href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/Documentation/filesystems/proc.rst?h=v6.0.3&quot;&gt;kernel doc &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proc.txt&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;We are in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nonroot&lt;/code&gt; home directory and our environment reflects the user
environment.&lt;/li&gt;
  &lt;li&gt;We have our binaries in a standard paths.  Following the &lt;a href=&quot;https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard&quot;&gt;Linux filesystem
hierarchy standard&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sbin/nologin&lt;/code&gt; will return non-zero if a user login is attempted.&lt;/li&gt;
  &lt;li&gt;As a user, we can write to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; filesystem verifying its permissions are
set correctly along with the sticky bit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;arm-support&quot;&gt;ARM support&lt;/h1&gt;

&lt;p&gt;This minimal Docker image is meant to be cross platform for both AMD64 and
ARM64.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker buildx build --platform linux/arm64 --build-arg base=arm64v8/alpine -t minimal-arm .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or if you’re on a computer which is already native ARM you can run the original
docker command and it should build just fine.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker build -t minimal .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;full-docker-example-with-tls-ca-and-tzinfo&quot;&gt;Full Docker example with TLS CA and tzinfo&lt;/h1&gt;

&lt;p&gt;I work a lot with Amazon web services.  In my case, it makes sense to copy
certificates and timezone information from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;amazonlinux:2&lt;/code&gt;.  However, if you’re in
another cloud provider or data center; then use whatever base image of your
choice.  Copying these paths are pretty standard for nearly any Linux
distribution.&lt;/p&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;ARG&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; base=alpine&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ${base}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/bin/sh&quot;, &quot;-exc&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# Prerequisites&lt;/span&gt;
  apk add --no-cache build-base; \
  # Directory structure and permissions
  mkdir -p base/bin base/tmp base/var/tmp base/etc base/home/nonroot base/sbin base/root; \
  chmod 700 /root; \
  chown root:root /root; \
  chmod 1777 base/tmp base/var/tmp; \
  chown 65532:65532 base/home/nonroot; \
  chmod 750 base/home/nonroot; \
  # UID and GID
  echo &apos;root:x:0:&apos; &amp;gt; /base/etc/group; \
  echo &apos;nonroot:x:65532:&apos; &amp;gt;&amp;gt; /base/etc/group; \
  echo &apos;root:x:0:0:root:/root:/sbin/nologin&apos; &amp;gt; /base/etc/passwd; \
  echo &apos;nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin&apos; &amp;gt;&amp;gt; /base/etc/passwd; \
  # init binary
  wget -O base/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_&quot;`uname -m`&quot;; \
  chmod 755 base/bin/dumb-init; \
  # nologin binary
  echo &apos;int main() { return 1; }&apos; &amp;gt; nologin.c; \
  gcc -Os -no-pie -static -std=gnu99 -s -Wall -Werror -o base/sbin/nologin nologin.c; \
  echo &quot;Minimal Container version $VERSION&quot; &amp;gt; /etc/issue


&lt;span class=&quot;c&quot;&gt;# Add our example program (bash)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Note: you don&apos;t need this for your own application.  In this case static bash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#   is the example application running in user context within a minimal image&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Comment out these lines and update CMD for your own app.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  wget &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; base/bin/bash https://github.com/robxu9/bash-static/releases/download/5.1.016-1.2.3/bash-linux-&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;uname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;755 base/bin/bash

&lt;span class=&quot;c&quot;&gt;# Pull TLS certifactes and timezone info from amazon&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; amazonlinux:2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; base/etc base/usr/share&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; /etc/ssl /etc/pki base/etc/&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; /usr/share/zoneinfo base/usr/share/


&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; scratch&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=0 /base/ /&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=1 /base/ /&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/bin/dumb-init&quot;, &quot;--&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; nonroot&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; HOME=/home/nonroot USER=nonroot&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /home/nonroot&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/bin/bash&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;working-with-shared-libraries&quot;&gt;Working with shared libraries&lt;/h1&gt;

&lt;p&gt;I wrote a &lt;a href=&quot;https://github.com/samrocketman/home/blob/main/bin/copy-bin.sh&quot;&gt;script named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;copy-bin.sh&lt;/code&gt;&lt;/a&gt; which makes it easy to copy
binaries which were compiled with shared object dependencies.&lt;/p&gt;

&lt;p&gt;For example, if you wanted to copy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;busybox&lt;/code&gt; and all of its utility references
into an image created from scratch to inspect it; the following docker image
would apply.&lt;/p&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;alpine&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;busybox&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/bin/sh&quot;, &quot;-exc&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  wget &lt;span class=&quot;nt&quot;&gt;-qO&lt;/span&gt; /usr/local/bin/copy-bin.sh https://raw.githubusercontent.com/samrocketman/home/main/bin/copy-bin.sh&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;755 /usr/local/bin/copy-bin.sh
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  copy-bin.sh &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt; /base &lt;span class=&quot;nt&quot;&gt;--ldd&lt;/span&gt; /bin/busybox &lt;span class=&quot;nt&quot;&gt;--links&lt;/span&gt; /bin:/sbin:/usr/bin:/usr/sbin

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; scratch&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=busybox /base/ /&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/bin/sh&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the above content in a Dockerfile; create the image.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker build -t scratch-busybox -f Dockerfile.busybox .
docker run -it --rm scratch-busybox ls
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;If you’re building statically compiled binaries then you can rely on all of the
recommended best practices for Linux and Docker to have both a safer and smaller
Docker image than one provided to you by a Linux distribution.&lt;/p&gt;

&lt;p&gt;Here’s a small summary of the image sizes.  All sized exclude &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; assuming
you would remove it to replace it with a statically compiled application.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;76.4kB&lt;/code&gt; minimal example.  If your app doesn’t need CA certificates or
timezone information then you get all of the security and stability goodies at
a very small storage price.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.53MB&lt;/code&gt; when including TLS CA certificates and timezone information.&lt;/li&gt;
&lt;/ul&gt;

</description>
                <pubDate>Tue, 25 Oct 2022 00:00:00 -0400</pubDate>
                <link>https://sam.gleske.net/blog/engineering/2022/10/25/guide-to-production-docker-images.html</link>
                <guid isPermaLink="true">https://sam.gleske.net/blog/engineering/2022/10/25/guide-to-production-docker-images.html</guid>
            </item>
            <item>
                <title>Windows on Linux: SENA and USB Device Support</title>
                <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#introduction-and-audience&quot; id=&quot;markdown-toc-introduction-and-audience&quot;&gt;Introduction and Audience&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#audience&quot; id=&quot;markdown-toc-audience&quot;&gt;Audience&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#why-windows-on-linux&quot; id=&quot;markdown-toc-why-windows-on-linux&quot;&gt;Why Windows on Linux?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#about-software&quot; id=&quot;markdown-toc-about-software&quot;&gt;About Software&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#my-machine&quot; id=&quot;markdown-toc-my-machine&quot;&gt;My machine&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#install-virtualbox-and-vagrant&quot; id=&quot;markdown-toc-install-virtualbox-and-vagrant&quot;&gt;Install VirtualBox and Vagrant&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#download-windows&quot; id=&quot;markdown-toc-download-windows&quot;&gt;Download Windows&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#add-windows-to-vagrant&quot; id=&quot;markdown-toc-add-windows-to-vagrant&quot;&gt;Add Windows to Vagrant&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#provisioning-windows&quot; id=&quot;markdown-toc-provisioning-windows&quot;&gt;Provisioning Windows&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#vagrantfile&quot; id=&quot;markdown-toc-vagrantfile&quot;&gt;Vagrantfile&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#first-time-windows-setup&quot; id=&quot;markdown-toc-first-time-windows-setup&quot;&gt;First-time Windows setup&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#starting-and-stopping-windows&quot; id=&quot;markdown-toc-starting-and-stopping-windows&quot;&gt;Starting and stopping Windows&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#adding-usb-3-support&quot; id=&quot;markdown-toc-adding-usb-3-support&quot;&gt;Adding USB 3 support&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#attaching-usb-devices-to-virtualbox&quot; id=&quot;markdown-toc-attaching-usb-devices-to-virtualbox&quot;&gt;Attaching USB Devices to VirtualBox&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;introduction-and-audience&quot;&gt;Introduction and Audience&lt;/h1&gt;

&lt;p&gt;This article is loosely based on &lt;a href=&quot;https://gist.github.com/samrocketman/4749ef939f6e43e32923fe608de5bb07&quot;&gt;a GitHub gist I wrote&lt;/a&gt; for updating my
SENA, but this includes more convenience automation via Vagrant; details to
follow.&lt;/p&gt;

&lt;p&gt;This post describes how to provision Windows on Linux for free.  This process is
legal and does not involve illegally downloading software.  Microsoft provides
free Windows virtual machines through its &lt;em&gt;Microsoft Edge Developer&lt;/em&gt; program at
no cost to you, the user.&lt;/p&gt;

&lt;p&gt;Minimum specifications to follow along with this article:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;4 core CPU which has hardware virtualization extensions (check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/proc/cpuinfo&lt;/code&gt;
for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vme&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vmx&lt;/code&gt;, or both).  You may have to enable virtualization in your
BIOS or UEFI which is not covered in this article.&lt;/li&gt;
  &lt;li&gt;16GB RAM.&lt;/li&gt;
  &lt;li&gt;30GB harddrive space free.  Windows 10 is pretty large and you’ll require
three times the size of Windows.  You’ll need three times the size because
you’ll download a Windows virtual machine as a zip, extract the zip, and then
install it within Vagrant.  You can clean up the download files when you’re
done which will free up 14GB of space.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;audience&quot;&gt;Audience&lt;/h3&gt;

&lt;p&gt;I’ll assume you use Linux day to day and have some familiarization with computer
hardware.  While these instructions attempt to take into account beginners to
Linux it is not geared towards a beginner.  If you’re new to Linux and this
article is confusing, then I recommend the following tutorials.  Once you’ve
gone through them you can come back to this page and try again.&lt;/p&gt;

&lt;p&gt;Linux Journey web tutorials:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Grasshopper: &lt;a href=&quot;https://linuxjourney.com/lesson/the-shell&quot;&gt;Command Line&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Grasshopper: &lt;a href=&quot;https://linuxjourney.com/lesson/regular-expressions-regex&quot;&gt;Advanced Text-Fu&lt;/a&gt; although I personally like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt;.  If
you install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; on your machine, then you can run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vimtutor&lt;/code&gt; command to
learn how to use it.&lt;/li&gt;
  &lt;li&gt;Journeyman: &lt;a href=&quot;https://linuxjourney.com/lesson/dev-directory&quot;&gt;Devices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;why-windows-on-linux&quot;&gt;Why Windows on Linux?&lt;/h3&gt;

&lt;p&gt;It is a reality that I’ll have some piece of hardware such as a Garmin
GPS or a SENA Motorcycle headset which needs firmware updates but only works
from Windows or Mac.  This guide is intended to provide instruction on how to
connect such hardware and update it from Linux using a Windows virtual machine
at no cost of ownership.&lt;/p&gt;

&lt;h3 id=&quot;about-software&quot;&gt;About Software&lt;/h3&gt;

&lt;p&gt;This article will use a mix of free and proprietary software; all of which costs
no money to use.  The following is a summarized list.  Refer to the website of
each software for their given licensing and usage.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/&quot;&gt;Microsoft Windows&lt;/a&gt;.  No explanation required, I think.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.vagrantup.com/&quot;&gt;Vagrant&lt;/a&gt; is software used to automate provisioning virtual machines.
It provides convenience around installation and booting of virtual machines
for VirtualBox.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.virtualbox.org/&quot;&gt;VirtualBox&lt;/a&gt; is a hypervisor and virtual machine technology.  It is
widely availabot for Linux, Mac, and Windows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;my-machine&quot;&gt;My machine&lt;/h3&gt;

&lt;p&gt;The following are the specifications for the machine I used for testing commands
and writing this blog post.&lt;/p&gt;

&lt;p&gt;Software specifications:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;uname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rms&lt;/span&gt;
Linux 5.4.0-81-generic x86_64

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;lsb_release &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;
LSB Version:	core-9.20170808ubuntu1-noarch:printing-9.20170808ubuntu1-noarch:security-9.20170808ubuntu1-noarch
Distributor ID:	Ubuntu
Description:	Pop!_OS 18.04 LTS
Release:	18.04
Codename:	bionic

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;bash &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n1&lt;/span&gt;
GNU bash, version 4.4.20&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-release&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;x86_64-pc-linux-gnu&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Hardware specifications:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make/model: system76 Darter Pro model darp5&lt;/li&gt;
  &lt;li&gt;Processor: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz 4 core/8 threads with
vme/vmx extensions.  See &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/proc/cpuinfo&lt;/code&gt; to check if your processor has these
virtualization extensions.&lt;/li&gt;
  &lt;li&gt;Memory: 32GB RAM&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;install-virtualbox-and-vagrant&quot;&gt;Install VirtualBox and Vagrant&lt;/h1&gt;

&lt;p&gt;You can find instructions for how to install VirtualBox and Vagrant from the
respective project website.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.virtualbox.org/wiki/Downloads&quot;&gt;Instructions for VirtualBox&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.vagrantup.com/downloads&quot;&gt;Instructions for Vagrant&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install VirtualBox on Pop OS 18.04 and Ubuntu 18.04.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;virtualbox virtualbox-dkms virtualbox-ext-pack virtualbox-guest-additions-iso virtualbox-qt
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;usermod &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-G&lt;/span&gt; vboxusers &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Changes made by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usermod&lt;/code&gt; command will not take effect until after
you start a new login session.  I recommend restarting your computer for
simplicity sake.  If you’re on another Linux OS you can check the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/group&lt;/code&gt;
file for the VirtualBox group you need to add to your user.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Installing Vagrant on Pop OS 18.04 and Ubuntu 18.04.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-fsSL&lt;/span&gt; https://apt.releases.hashicorp.com/gpg | &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-key add -
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-add-repository &lt;span class=&quot;s2&quot;&gt;&quot;deb [arch=amd64] https://apt.releases.hashicorp.com &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;lsb_release &lt;span class=&quot;nt&quot;&gt;-cs&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; main&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;vagrant
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll also want to install a plugin which automates installing VirtualBox Guest
Additions.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vagrant plugin install vagrant-vbguest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;download-windows&quot;&gt;Download Windows&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Visit &lt;a href=&quot;https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/&quot;&gt;Microsoft Edge Developer Virtual Machines page&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Under Virtual Machines choose MSEdge on Win10 x64.&lt;/li&gt;
  &lt;li&gt;Choose VM Platform: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vagrant&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Check your Downloads folder for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MSEdge.Win10.Vagrant.zip&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;add-windows-to-vagrant&quot;&gt;Add Windows to Vagrant&lt;/h3&gt;

&lt;p&gt;Go to your downloads directory and unzip the Zip file.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ~/Downloads/
unzip MSEdge.Win10.Vagrant.zip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Add the resulting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.box&lt;/code&gt; file to Vagrant with a name of your choice.  Since
Microsoft offers multiple versions of Windows I recommend naming the box similar
to the version of Windows you’ve downloaded.  This will give you additional
flexibility for importing additional versions of Windows.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vagrant box add ./MSEdge\ -\ Win10.box --name windows/10edge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;provisioning-windows&quot;&gt;Provisioning Windows&lt;/h1&gt;

&lt;p&gt;Windows will be automatically provisioned by using Vagrant.  Vagrant automates
booting and installing VirtualBox virtual machines.  Vagrant uses a file named
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vagrantfile&lt;/code&gt; to describe a virtual machine and its operating system.&lt;/p&gt;

&lt;h3 id=&quot;vagrantfile&quot;&gt;Vagrantfile&lt;/h3&gt;

&lt;p&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows&lt;/code&gt; directory which will simplify managing your Vagrant virtual
machine.  All following commands will assume this working directory.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mkdir ~/Windows
cd ~/Windows/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Create a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Windows/Vagrantfile&lt;/code&gt;.  Ensure it has the following
contents.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Vagrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;windows/10edge&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;box_check_update&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Windows remote management settings&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;guest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:windows&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;communicator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;winrm&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;winrm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IEUser&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;winrm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Passw0rd!&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Vagrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;has_plugin?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;vagrant-vbguest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vbguest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;auto_update&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;virtualbox&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Hardware settings&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gui&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cpus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;4&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;memory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;8192&quot;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Operating system&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;customize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;modifyvm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--ostype&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Windows10_64&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Video Settings with remote desktop disabled&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;customize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;modifyvm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--vram&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--accelerate3d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;on&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--vrde&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;off&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# DVD Drive for VBox Guest Additions&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;customize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;storageattach&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--storagectl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IDE Controller&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--port&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--device&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dvddrive&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--medium&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;emptydrive&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# USB 3 support; it should only run initially&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exists?&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;usb-setup-complete&quot;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;vb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;customize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;storagectl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;USB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--add&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;usb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--controller&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;USB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--hostiocache&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;on&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;vb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;customize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;modifyvm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--usb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;on&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;--usbxhci&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;on&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Switch logic for USB 3 support&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:up&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Checking usb-setup-complete...&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;inline: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bash -c &apos;[ -f usb-setup-complete ] || touch usb-setup-complete&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:destroy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Removing usb-setup-complete...&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;inline: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bash -c &apos;[ ! -f usb-setup-complete ] || rm -f usb-setup-complete&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; if you use another version of Windows be sure to change the
operating system &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--ostype&lt;/code&gt;.  You can view a list of all supported OS types by
running the following command in your terminal.&lt;/p&gt;

  &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage list ostypes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;first-time-windows-setup&quot;&gt;First-time Windows setup&lt;/h3&gt;

&lt;p&gt;First-time setup only needs to be performed on initial provisioning and does not
need to be repeated for the life of the virtual machine.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Start Windows via Vagrant.&lt;/li&gt;
  &lt;li&gt;Log into Windows and enable Windows Remote Management.&lt;/li&gt;
  &lt;li&gt;Ensure VirtualBox Guest Additions is installed.  This is done automatically,
but you want to make sure it was successful.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Provision and start Windows with the following command.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vagrant up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As soon as the Windows login screen is visible log into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEUser&lt;/code&gt; using the
password: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Passw0rd!&lt;/code&gt;.  From the start menu search for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cmd&lt;/code&gt;.  Right click on
&lt;em&gt;Command Prompt&lt;/em&gt; and select &lt;em&gt;Run as administrator&lt;/em&gt;.  From the administrator
command prompt run the following command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;WinRM quickconfig
sc config WinRM start=auto
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Choose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Y&lt;/code&gt; to enable Windows Remote Management.  The second command will force
Windows Remote Management to always autostart without delay.  From this point,
Vagrant should automatically continue with setup and installation of VirtualBox
Guest Additions.&lt;/p&gt;

&lt;p&gt;This log shows the full output when you’ve completed all of the steps
successfully.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ vagrant up
Bringing machine &apos;default&apos; up with &apos;virtualbox&apos; provider...
==&amp;gt; default: Importing base box &apos;windows/10edge&apos;...
==&amp;gt; default: Matching MAC address for NAT networking...
==&amp;gt; default: Setting the name of the VM: Windows_default_1629924069660_49626
==&amp;gt; default: Clearing any previously set network interfaces...
==&amp;gt; default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==&amp;gt; default: Forwarding ports...
    default: 5985 (guest) =&amp;gt; 55985 (host) (adapter 1)
    default: 5986 (guest) =&amp;gt; 55986 (host) (adapter 1)
    default: 22 (guest) =&amp;gt; 2222 (host) (adapter 1)
==&amp;gt; default: Running &apos;pre-boot&apos; VM customizations...
==&amp;gt; default: Booting VM...
==&amp;gt; default: Waiting for machine to boot. This may take a few minutes...
    default: WinRM address: 127.0.0.1:55985
    default: WinRM username: IEUser
    default: WinRM execution_time_limit: PT2H
    default: WinRM transport: negotiate
==&amp;gt; default: Machine booted and ready!
==&amp;gt; default: Checking for guest additions in VM...
    default: The guest additions on this VM do not match the installed version of
    default: VirtualBox! In most cases this is fine, but in rare cases it can
    default: prevent things such as shared folders from working properly. If you see
    default: shared folder errors, please make sure the guest additions within the
    default: virtual machine match the version of VirtualBox you have installed on
    default: your host and reload your VM.
    default: 
    default: Guest Additions Version: 6.0.4
    default: VirtualBox Version: 5.2
==&amp;gt; default: Mounting shared folders...
    default: /vagrant =&amp;gt; /home/sam/Windows
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should see VirtualBox Guest Additions running from the Windows system tray
in the bottom right corner of the OS.&lt;/p&gt;

&lt;h3 id=&quot;starting-and-stopping-windows&quot;&gt;Starting and stopping Windows&lt;/h3&gt;

&lt;p&gt;Always start Windows using Vagrant and shut down Windows from within the VM.  To
start Windows run the following command.  You must be in the same directory as
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vagrantfile&lt;/code&gt; for all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vagrant&lt;/code&gt; commands to succeed.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cd ~/Windows/
vagrant up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At the Windows login screen, enter the IEUser password: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Passw0rd!&lt;/code&gt;.  This
password is also mentioned on the &lt;a href=&quot;https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/&quot;&gt;Microsoft Edge Developer Virtual
Machines&lt;/a&gt; webpage.&lt;/p&gt;

&lt;p&gt;To shut down Windows gracefully, run the following command.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vagrant halt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; you can also shut Windows down from its start menu.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you would like to completely delete the Windows virtual machine, then you
must destroy it.  The following command will permanently delete Windows but it
will still be available for provisioning through Vagrant.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vagrant destroy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;adding-usb-3-support&quot;&gt;Adding USB 3 support&lt;/h1&gt;

&lt;p&gt;Windows 10 was provisioned with USB 3 support enabled.  If you’d like to connect
your USB hardware to Windows, then you’ll need to attach the device through
VirtualBox.&lt;/p&gt;

&lt;h3 id=&quot;attaching-usb-devices-to-virtualbox&quot;&gt;Attaching USB Devices to VirtualBox&lt;/h3&gt;

&lt;p&gt;List all USB devices.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lsusb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are two pieces of information to take note when looking at the list of USB
devices.  The Vendor ID and Product ID.  You can use these to automatically
directly attach the USB device to Windows.&lt;/p&gt;

&lt;p&gt;For example, let’s take a look at my SENA hardware.  The following is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsusb&lt;/code&gt;
output.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Bus 003 Device 005: ID 092b:5530 Sena Technologies, Inc.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can get additional information about this USB device.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lsusb -d 092b:5530 -v
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Go to VirtualBox Manager (while the Windows computer is powered off). View
the virtual machine settings.&lt;/li&gt;
  &lt;li&gt;Click on USB and off to the right there are tiny icons for adding and
removing USB filters.&lt;/li&gt;
  &lt;li&gt;Add a new USB filter. In my case, I filled out the following settings.
    &lt;ul&gt;
      &lt;li&gt;Name: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sena&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;Vendor ID: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;092b&lt;/code&gt; (taken from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idVendor&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsusb&lt;/code&gt; output)&lt;/li&gt;
      &lt;li&gt;Product ID: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5530&lt;/code&gt; (taken from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idProduct&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsusb&lt;/code&gt; output)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The rest of the USB filter settings I left alone. The fewer details
you add to the filter the more broadly devices will match (e.g. you can just
specify only the Vendor ID and Manufacturer to match all SENA devices).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Power off and disconnect the USB device.  Reconnect and power on the device.
Because of the USB filter, the device will automatically connect directly to the
virtual machine for Windows to manage next time it is powered on.&lt;/p&gt;

</description>
                <pubDate>Wed, 25 Aug 2021 00:00:00 -0400</pubDate>
                <link>https://sam.gleske.net/blog/engineering/2021/08/25/windows-on-linux-sena-firmware.html</link>
                <guid isPermaLink="true">https://sam.gleske.net/blog/engineering/2021/08/25/windows-on-linux-sena-firmware.html</guid>
            </item>
            <item>
                <title>Fixing Ubuntu Linux hang on boot after upgrade</title>
                <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#scenario&quot; id=&quot;markdown-toc-scenario&quot;&gt;Scenario&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#attempted-fix&quot; id=&quot;markdown-toc-attempted-fix&quot;&gt;Attempted fix&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#permanent-fix&quot; id=&quot;markdown-toc-permanent-fix&quot;&gt;Permanent Fix&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;scenario&quot;&gt;Scenario&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;I have mostly been using my Laptop the past several months.&lt;/li&gt;
  &lt;li&gt;After a few months I attempted to boot my Desktop which has Ubuntu 18.04.&lt;/li&gt;
  &lt;li&gt;It would hang on boot with the following message.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Done] Started hold until boot process finishes up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;attempted-fix&quot;&gt;Attempted fix&lt;/h1&gt;

&lt;p&gt;I booted into Ubuntu recovery mode successfully via the boot option: advanced
options.  I enabled networking and then connected to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; terminal.&lt;/p&gt;

&lt;p&gt;First, upgraded packages.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get update
apt-get upgrade
apt-get install &amp;lt;held back packages&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Second, I removed some snaps which appeared to be giving me trouble.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;snap list
snap remove &amp;lt;package&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also refreshed the core snaps which basically upgraded them to latest.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;snap refresh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;None of it seemed to fix the issue.&lt;/p&gt;

&lt;h1 id=&quot;permanent-fix&quot;&gt;Permanent Fix&lt;/h1&gt;

&lt;p&gt;After web searching, I realized Wayland may be loaded by GDM.  I found a &lt;a href=&quot;https://askubuntu.com/questions/1084550/ubuntu-18-10-stuck-on-started-bpfilter-while-booting/1085596#1085596&quot;&gt;good
stack overflow post which addressed fixing the issue&lt;/a&gt;.  Edit the file
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/gdm3/custom.conf&lt;/code&gt; and uncomment the following line.&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[daemon]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;WaylandEnable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This forced GNOME Desktop Manager 3 to use Xorg instead of Wayland.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;This isn’t the first time Wayland has caused me issues.  My opinion is not very
high of it.  I like to play steam games through Proton and other games through
WINE.  However, I seem to always have issues with Wayland even when using
XWayland.&lt;/p&gt;

&lt;p&gt;I’ve been avoiding Ubuntu 20.04 because of Wayland… When Ubuntu 18.04 loses
upgrade support may be the day I stop using Ubuntu unless I can verify gaming
on Linux is stable when I’m ready for the next edition.  I’ll probably hold out
for Ubuntu 22.04 and evaluate it for gaming when the time comes.&lt;/p&gt;

</description>
                <pubDate>Mon, 17 May 2021 00:00:00 -0400</pubDate>
                <link>https://sam.gleske.net/blog/gaming/2021/05/17/ubuntu-linux-hang-on-boot-after-upgrade.html</link>
                <guid isPermaLink="true">https://sam.gleske.net/blog/gaming/2021/05/17/ubuntu-linux-hang-on-boot-after-upgrade.html</guid>
            </item>
    </channel>
</rss>
