494 lines
26 KiB
HTML
494 lines
26 KiB
HTML
<!DOCTYPE html>
|
|
|
|
|
|
<html class="no-js" lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Why is DNS still hard to learn?</title>
|
|
<meta name="author" content="Julia Evans">
|
|
<meta name="HandheldFriendly" content="True">
|
|
<meta name="MobileOptimized" content="320">
|
|
<meta name="description" content="Why is DNS still hard to learn?">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
<meta property="og:title" content='Why is DNS still hard to learn?'>
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:url" content="https://jvns.ca/blog/2023/07/28/why-is-dns-still-hard-to-learn/" />
|
|
<meta property="og:site_name" content="Julia Evans" />
|
|
|
|
<link rel="canonical" href="https://jvns.ca/blog/2023/07/28/why-is-dns-still-hard-to-learn/">
|
|
<link href="/favicon.ico" rel="icon">
|
|
|
|
<link href="/stylesheets/screen.css" rel="preload" type="text/css" as="style">
|
|
|
|
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
|
|
<link href="/stylesheets/print.css" media="print" rel="stylesheet" type="text/css">
|
|
|
|
|
|
<link href="/atom.xml" rel="alternate" title="Julia Evans" type="application/atom+xml">
|
|
|
|
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" integrity="sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0" crossorigin="anonymous">
|
|
<script defer data-domain="jvns.ca" src="https://plausible.io/js/script.js"></script>
|
|
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js" integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4" crossorigin="anonymous"></script>
|
|
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script>
|
|
|
|
<script defer type="text/javascript">
|
|
window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=document.createElement("script");r.type="text/javascript",r.async=!0,r.src="https://cdn.heapanalytics.com/js/heap-"+e+".js";var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(r,a);for(var n=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","resetIdentity","removeEventProperty","setEventProperties","track","unsetEventProperty"],o=0;o<p.length;o++)heap[p[o]]=n(p[o])};
|
|
heap.load("2242143965");
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="skiptocontent">
|
|
<a href="#main">Skip to main content</a>
|
|
</div>
|
|
<div id="wrap">
|
|
<header role="banner">
|
|
<hgroup>
|
|
<h1><a href="/">Julia Evans</a></h1>
|
|
</hgroup>
|
|
<ul class="header-links">
|
|
<li><a href="/about">About</a></li>
|
|
<li><a href="/talks">Talks</a></li>
|
|
<li><a href="/projects/">Projects</a></li>
|
|
<li><a rel="me" href="https://social.jvns.ca/@b0rk">Mastodon</a></li>
|
|
<li><a href="https://bsky.app/profile/b0rk.jvns.ca">Bluesky</a></li>
|
|
<li><a href="https://github.com/jvns">Github</a></li>
|
|
</ul>
|
|
</header>
|
|
<nav role="navigation" class="header-nav"><ul class="main-navigation">
|
|
<li><a href="/categories/favorite/">Favorites</a></li>
|
|
<li><a href="/til/">TIL</a></li>
|
|
<li><a href="https://wizardzines.com">Zines</a></li>
|
|
<li class="subscription" data-subscription="rss"><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
|
|
</ul>
|
|
</nav>
|
|
<div id="main">
|
|
<div id="content">
|
|
|
|
|
|
<div>
|
|
<article class="hentry" role="article">
|
|
<header>
|
|
<h1 class="entry-title">Why is DNS still hard to learn?</h1>
|
|
|
|
<div class="post-tags">
|
|
|
|
•
|
|
|
|
<a class="post-tag" href="/categories/dns">dns</a> •
|
|
|
|
|
|
</div>
|
|
<p class="meta sans">
|
|
<time class="date" datetime="2023-07-28T09:40:04" pubdate data-updated="true">
|
|
|
|
July 28, 2023
|
|
|
|
</time>
|
|
</p>
|
|
</header>
|
|
<main>
|
|
<p>I write a lot about technologies that I found hard to learn about. A
|
|
while back my friend Sumana asked me an interesting question – why are these
|
|
things so hard to learn about? Why do they seem so mysterious?</p>
|
|
<p>For example, take DNS. We’ve been using DNS since the <a href="https://www.ietf.org/rfc/rfc1034.txt">80s</a> (for more than 35 years!). It’s
|
|
used in every website on the internet. And it’s pretty stable – in a lot of
|
|
ways, it works the exact same way it did 30 years ago.</p>
|
|
<p>But it took me YEARS to figure out how to confidently debug DNS issues, and
|
|
I’ve seen a lot of other programmers struggle with debugging DNS problems as
|
|
well. So what’s going on?</p>
|
|
<p>Here are a couple of thoughts about why learning to troubleshoot DNS problems
|
|
is hard.</p>
|
|
<p>(I’m not going to explain DNS very much in this post, see <a href="https://implement-dns.wizardzines.com/">Implement DNS in a Weekend</a> or <a href="https://jvns.ca/categories/dns/">my DNS blog posts</a> for more about how DNS works)</p>
|
|
<h3 id="it-s-not-because-dns-is-super-hard" class="post-heading">
|
|
<a href="#it-s-not-because-dns-is-super-hard">
|
|
it’s not because DNS is super hard
|
|
</a>
|
|
</h3>
|
|
<p>When I finally learned how to troubleshoot DNS problems, my reaction was “what,
|
|
that was it???? that’s not that hard!”. I felt a little bit cheated! I could
|
|
explain to you everything that I found confusing about DNS in <a href="https://wizardzines.com/zines/dns">a few hours</a>.</p>
|
|
<p>So – if DNS is not all that complicated, why did it take me so many years to
|
|
figure out how to troubleshoot pretty basic DNS issues (like “my domain doesn’t
|
|
resolve even though I’ve set it up correctly” or “<code>dig</code> and my browser have
|
|
different DNS results, why?”)?</p>
|
|
<p>And I wasn’t alone in finding DNS hard to learn! I’ve talked to a lot of
|
|
smart friends who are very experienced programmers about DNS of the years, and
|
|
many of them either:</p>
|
|
<ul>
|
|
<li>didn’t feel comfortable making simple DNS changes to their websites</li>
|
|
<li>or were confused about basic facts about how DNS works (like that records are <a href="https://jvns.ca/blog/2021/12/06/dns-doesn-t-propagate/">pulled and not pushed</a>)</li>
|
|
<li>or did understand DNS basics pretty well, but had the some of the same
|
|
knowledge gaps that I’d struggled with (negative caching and the details of
|
|
how <code>dig</code> and your browser do DNS queries differently)</li>
|
|
</ul>
|
|
<p>So if we’re all struggling with the same things about DNS, what’s going on? Why
|
|
is it so hard to learn for so many people?</p>
|
|
<p>Here are some ideas.</p>
|
|
<h3 id="a-lot-of-the-system-is-hidden" class="post-heading">
|
|
<a href="#a-lot-of-the-system-is-hidden">
|
|
a lot of the system is hidden
|
|
</a>
|
|
</h3>
|
|
<p>When you make a DNS request on your computer, the basic story is:</p>
|
|
<ol>
|
|
<li>your computer makes a request to a server called <strong>resolver</strong></li>
|
|
<li>the resolver checks its cache, and makes requests to some other servers called <strong>authoritative nameservers</strong></li>
|
|
</ol>
|
|
<p>Here are some things you don’t see:</p>
|
|
<ul>
|
|
<li>the resolver’s <strong>cache</strong>. What’s in there?</li>
|
|
<li>which <strong>library code</strong> on your computer is making the DNS request (is it libc
|
|
<code>getaddrinfo</code>? if so, is it the getaddrinfo from glibc, or musl, or apple? is
|
|
it your browser’s DNS code? is it a different custom DNS implementation?).
|
|
All of these options behave slightly differently and have different
|
|
configuration, approaches to caching, available features, etc. For example musl DNS didn’t support TCP until <a href="https://www.theregister.com/2023/05/16/alpine_linux_318/">early 2023</a>.</li>
|
|
<li>the <strong>conversation</strong> between the resolver and the authoritative nameservers. I
|
|
think a lot of DNS issues would be SO simple to understand if you could
|
|
magically get a trace of exactly which authoritative nameservers were
|
|
queried downstream during your request, and what they said. (like, what if
|
|
you could run <code>dig +debug google.com</code> and it gave you a bunch of extra
|
|
debugging information?)</li>
|
|
</ul>
|
|
<h3 id="dealing-with-hidden-systems" class="post-heading">
|
|
<a href="#dealing-with-hidden-systems">
|
|
dealing with hidden systems
|
|
</a>
|
|
</h3>
|
|
<p>A couple of ideas for how to deal with hidden systems</p>
|
|
<ul>
|
|
<li>just teaching people what the hidden systems are makes a huge difference. For
|
|
a long time I had no idea that my computer had many different DNS libraries
|
|
that were used in different situations and I was confused about this for
|
|
literally years. This is a big part of my approach.</li>
|
|
<li>with <a href="https://messwithdns.net/">Mess With DNS</a> we tried out this “fishbowl”
|
|
approach where it shows you some parts of the system (the conversation with
|
|
the resolver and the authoritative nameserver) that are normally hidden</li>
|
|
<li>I feel like it would be extremely cool to extend DNS to include a “debugging
|
|
information” section. (edit: it looks like this already exists! It’s called
|
|
<a href="https://blog.nlnetlabs.nl/extended-dns-error-support-for-unbound/">Extended DNS Errors</a>,
|
|
or EDE, and tools are slowly adding support for it.</li>
|
|
</ul>
|
|
<h3 id="extended-dns-errors-seem-cool" class="post-heading">
|
|
<a href="#extended-dns-errors-seem-cool">
|
|
Extended DNS Errors seem cool
|
|
</a>
|
|
</h3>
|
|
<p>Extended DNS Errors are a new way for DNS servers to provide extra debugging information in DNS response. Here’s an example of what that looks like:</p>
|
|
<pre><code>$ dig @8.8.8.8 xjwudh.com
|
|
;; Got answer:
|
|
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 39830
|
|
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
|
|
|
|
;; OPT PSEUDOSECTION:
|
|
; EDNS: version: 0, flags:; udp: 512
|
|
; EDE: 12 (NSEC Missing): (Invalid denial of existence of xjwudh.com/a)
|
|
;; QUESTION SECTION:
|
|
;xjwudh.com. IN A
|
|
|
|
;; AUTHORITY SECTION:
|
|
com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1690634120 1800 900 604800 86400
|
|
|
|
;; Query time: 92 msec
|
|
;; SERVER: 8.8.8.8#53(8.8.8.8) (UDP)
|
|
;; WHEN: Sat Jul 29 08:35:45 EDT 2023
|
|
;; MSG SIZE rcvd: 161
|
|
</code></pre>
|
|
<p>Here I’ve requested a nonexistent domain, and I got the extended error <code>EDE: 12 (NSEC Missing): (Invalid denial of existence of xjwudh.com/a)</code>. I’m not
|
|
sure what that means (it’s some DNSSEC Thing), but it’s cool to see an extra
|
|
debug message like that.</p>
|
|
<p>I did have to install a newer version of <code>dig</code> to get the above to work.</p>
|
|
<h3 id="confusing-tools" class="post-heading">
|
|
<a href="#confusing-tools">
|
|
confusing tools
|
|
</a>
|
|
</h3>
|
|
<p>Even though a lot of DNS stuff is hidden, there are a lot of ways to figure out
|
|
what’s going on by using <code>dig</code>.</p>
|
|
<p>For example, you can use <code>dig +norecurse</code> to figure out if a given DNS resolver
|
|
has a particular record in its cache. <code>8.8.8.8</code> seems to return a <code>SERVFAIL</code>
|
|
response if the response isn’t cached.</p>
|
|
<p>here’s what that looks like for <code>google.com</code></p>
|
|
<pre><code>$ dig +norecurse @8.8.8.8 google.com
|
|
;; Got answer:
|
|
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11653
|
|
;; flags: qr ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
|
|
|
|
;; OPT PSEUDOSECTION:
|
|
; EDNS: version: 0, flags:; udp: 512
|
|
;; QUESTION SECTION:
|
|
;google.com. IN A
|
|
|
|
;; ANSWER SECTION:
|
|
google.com. 21 IN A 172.217.4.206
|
|
|
|
;; Query time: 57 msec
|
|
;; SERVER: 8.8.8.8#53(8.8.8.8)
|
|
;; WHEN: Fri Jul 28 10:50:45 EDT 2023
|
|
;; MSG SIZE rcvd: 55
|
|
</code></pre>
|
|
<p>and for <code>homestarrunner.com</code>:</p>
|
|
<pre><code>$ dig +norecurse @8.8.8.8 homestarrunner.com
|
|
;; Got answer:
|
|
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 55777
|
|
;; flags: qr ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
|
|
|
|
;; OPT PSEUDOSECTION:
|
|
; EDNS: version: 0, flags:; udp: 512
|
|
;; QUESTION SECTION:
|
|
;homestarrunner.com. IN A
|
|
|
|
;; Query time: 52 msec
|
|
;; SERVER: 8.8.8.8#53(8.8.8.8)
|
|
;; WHEN: Fri Jul 28 10:51:01 EDT 2023
|
|
;; MSG SIZE rcvd: 47
|
|
</code></pre>
|
|
<p>Here you can see we got a normal <code>NOERROR</code> response for <code>google.com</code> (which is
|
|
in <code>8.8.8.8</code>’s cache) but a <code>SERVFAIL</code> for <code>homestarrunner.com</code> (which isn’t).
|
|
This doesn’t mean there’s no DNS record <code>homestarrunner.com</code> (there is!), it’s
|
|
just not cached).</p>
|
|
<p>But this output is really confusing to read if you’re not used to it! Here are a few things that I think are weird about it:</p>
|
|
<ol>
|
|
<li>the headings are weird (there’s <code>->>HEADER<<-</code>, <code>flags:</code>, <code>OPT PSEUDOSECTION:</code>, <code>QUESTION SECTION:</code>, <code>ANSWER SECTION:</code>)</li>
|
|
<li>the spacing is weird (why is the no newline between <code>OPT PSEUDOSECTION</code> and <code>QUESTION SECTION</code>?)</li>
|
|
<li><code>MSG SIZE rcvd: 47</code> is weird (are there other fields in <code>MSG SIZE</code> other than <code>rcvd</code>? what are they?)</li>
|
|
<li>it says that there’s 1 record in the ADDITIONAL section but doesn’t show it, you have to somehow magically know that the “OPT PSEUDOSECTION” record is actually in the additional section</li>
|
|
</ol>
|
|
<p>In general <code>dig</code>’s output has the feeling of a script someone wrote in an adhoc
|
|
way that grew organically over time and not something that was intentionally
|
|
designed.</p>
|
|
<h3 id="dealing-with-confusing-tools" class="post-heading">
|
|
<a href="#dealing-with-confusing-tools">
|
|
dealing with confusing tools
|
|
</a>
|
|
</h3>
|
|
<p>some ideas for improving on confusing tools:</p>
|
|
<ul>
|
|
<li><strong>explain the output</strong>. For example I wrote <a href="https://jvns.ca/blog/2021/12/04/how-to-use-dig/">how to use dig</a> explaining how <code>dig</code>’s
|
|
output works and how to configure it to give you a shorter output by default</li>
|
|
<li><strong>make new, more friendly tools</strong>. For example for DNS there’s
|
|
<a href="https://github.com/ogham/dog">dog</a> and <a href="https://github.com/mr-karan/doggo">doggo</a> and <a href="https://dns-lookup.jvns.ca/">my dns lookup tool</a>. I think these are really cool but
|
|
personally I don’t use them because sometimes I want to do something a little
|
|
more advanced (like using <code>+norecurse</code>) and as far as I can tell neither
|
|
<code>dog</code> nor <code>doggo</code> support <code>+norecurse</code>. I’d rather use 1 tool for everything,
|
|
so I stick to <code>dig</code>. Replacing the breadth of functionality of <code>dig</code> is a
|
|
huge undertaking.</li>
|
|
<li><strong>make dig’s output a little more friendly</strong>. If I were better at C programming,
|
|
I might try to write a <code>dig</code> pull request that adds a <code>+human</code> flag to dig
|
|
that formats the long form output in a more structured and readable way,
|
|
maybe something like this:</li>
|
|
</ul>
|
|
<pre><code>$ dig +human +norecurse @8.8.8.8 google.com
|
|
HEADER:
|
|
opcode: QUERY
|
|
status: NOERROR
|
|
id: 11653
|
|
flags: qr ra
|
|
records: QUESTION: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
|
|
|
|
QUESTION SECTION:
|
|
google.com. IN A
|
|
|
|
ANSWER SECTION:
|
|
google.com. 21 IN A 172.217.4.206
|
|
|
|
ADDITIONAL SECTION:
|
|
EDNS: version: 0, flags:; udp: 512
|
|
|
|
EXTRA INFO:
|
|
Time: Fri Jul 28 10:51:01 EDT 2023
|
|
Elapsed: 52 msec
|
|
Server: 8.8.8.8:53
|
|
Protocol: UDP
|
|
Response size: 47 bytes
|
|
</code></pre>
|
|
<p>This makes the structure of the DNS response more clear – there’s the header, the
|
|
question, the answer, and the additional section.</p>
|
|
<p>And it’s not “dumbed down” or anything! It’s the exact same information, just
|
|
formatted in a more structured way. My biggest frustration with alternative DNS
|
|
tools that they often remove information in the name of clarity. And though
|
|
there’s definitely a place for those tools, I want to see all the information!
|
|
I just want it to be presented clearly.</p>
|
|
<p>We’ve learned a lot about how to design more user friendly command line tools
|
|
in the last 40 years and I think it would be cool to apply some of that
|
|
knowledge to some of our older crustier tools.</p>
|
|
<h3 id="dig-yaml" class="post-heading">
|
|
<a href="#dig-yaml">
|
|
dig +yaml
|
|
</a>
|
|
</h3>
|
|
<p>One quick note on dig: newer versions of dig do have a <code>+yaml</code> output format
|
|
which feels a little clearer to me, though it’s too verbose for my taste (a
|
|
pretty simple DNS response doesn’t fit on my screen)</p>
|
|
<h3 id="weird-gotchas" class="post-heading">
|
|
<a href="#weird-gotchas">
|
|
weird gotchas
|
|
</a>
|
|
</h3>
|
|
<p>DNS has some weird stuff that’s relatively common to run into, but pretty hard
|
|
to learn about if nobody tells you what’s going on. A few examples (there are more in <a href="https://jvns.ca/blog/2022/01/15/some-ways-dns-can-break/">some ways DNS can break</a>:</p>
|
|
<ul>
|
|
<li>negative caching! (which I talk about in <a href="https://jvns.ca/blog/2023/05/08/new-talk-learning-dns-in-10-years/">this talk</a>) It
|
|
took me probably 5 years to realize that I shouldn’t visit a domain that
|
|
doesn’t have a DNS record yet, because then the <strong>nonexistence</strong> of that
|
|
record will be cached, and it gets cached for HOURS, and it’s really
|
|
annoying.</li>
|
|
<li>differences in <code>getaddrinfo</code> implementations: until <a href="https://www.theregister.com/2023/05/16/alpine_linux_318/">early 2023</a>, <code>musl</code> didn’t support TCP DNS</li>
|
|
<li>resolvers that ignore TTLs: if you set a TTL on your DNS records (like “5
|
|
minutes”), some resolvers will ignore those TTLs completely and cache the
|
|
records for longer, like maybe 24 hours instead</li>
|
|
<li>if you configure nginx wrong (<a href="https://jvns.ca/blog/2022/01/15/some-ways-dns-can-break/#problem-nginx-caching-dns-records-forever">like this</a>), it’ll cache DNS records forever.</li>
|
|
<li>how <a href="https://pracucci.com/kubernetes-dns-resolution-ndots-options-and-why-it-may-affect-application-performances.html">ndots</a> can make your Kubernetes DNS slow</li>
|
|
</ul>
|
|
<h3 id="dealing-with-weird-gotchas" class="post-heading">
|
|
<a href="#dealing-with-weird-gotchas">
|
|
dealing with weird gotchas
|
|
</a>
|
|
</h3>
|
|
<p>I don’t have as good answers here as I would like to, but knowledge about weird
|
|
gotchas is extremely hard won (again, it took me years to figure out negative
|
|
caching!) and it feels very silly to me that people have to rediscover them for
|
|
themselves over and over and over again.</p>
|
|
<p>A few ideas:</p>
|
|
<ul>
|
|
<li>It’s incredibly helpful when people call out gotchas when explaining a topic. For example (leaving
|
|
DNS for a moment), Josh Comeau’s Flexbox intro explains this <a href="https://www.joshwcomeau.com/css/interactive-guide-to-flexbox/#the-minimum-size-gotcha-11">minimum size gotcha</a>
|
|
which I ran into SO MANY times for several years before finally finding an
|
|
explanation of what was going on.</li>
|
|
<li>I’d love to see more community collections of common gotchas. For bash,
|
|
<a href="https://www.shellcheck.net/">shellcheck</a> is an incredible collection of bash
|
|
gotchas.</li>
|
|
</ul>
|
|
<p>One tricky thing about documenting DNS gotchas is that different people are
|
|
going to run into different gotchas – if you’re just configuring DNS for your
|
|
personal domain once every 3 years, you’re probably going to run into different
|
|
gotchas than someone who administrates DNS for a domain with heavy traffic.</p>
|
|
<p>A couple of more quick reasons:</p>
|
|
<h3 id="infrequent-exposure" class="post-heading">
|
|
<a href="#infrequent-exposure">
|
|
infrequent exposure
|
|
</a>
|
|
</h3>
|
|
<p>A lot of people only deal with DNS extremely infrequently. And of course if you
|
|
only touch DNS every 3 years it’s going to be harder to learn!</p>
|
|
<p>I think cheat sheets (like “here are the steps to changing your nameservers”)
|
|
can really help with this.</p>
|
|
<h3 id="it-s-hard-to-experiment-with" class="post-heading">
|
|
<a href="#it-s-hard-to-experiment-with">
|
|
it’s hard to experiment with
|
|
</a>
|
|
</h3>
|
|
<p>DNS can be scary to experiment with – you don’t want to mess up your domain.
|
|
We built <a href="https://messwithdns.net/">Mess With DNS</a> to make this one a little easier.</p>
|
|
<h3 id="that-s-all-for-now" class="post-heading">
|
|
<a href="#that-s-all-for-now">
|
|
that’s all for now
|
|
</a>
|
|
</h3>
|
|
<p>I’d love to hear other thoughts about what makes DNS (or your favourite
|
|
mysterious technology) hard to learn.</p>
|
|
|
|
</main>
|
|
|
|
<footer>
|
|
|
|
<style type="text/css">
|
|
#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; display: inline;}
|
|
#mc_embed_signup {
|
|
display: inline;
|
|
}
|
|
#mc_embed_signup input.button {
|
|
background: #ff5e00;
|
|
display: inline;
|
|
color: white;
|
|
padding: 6px 12px;
|
|
}
|
|
</style>
|
|
<div class="sharing">
|
|
|
|
<style>
|
|
.form-inline {
|
|
display:flex; flex-flow: row wrap; justify-content: center;
|
|
}
|
|
.form-inline input, .form-inline span {
|
|
padding: 10px;
|
|
}
|
|
.form-inline input {
|
|
display:inline;
|
|
max-width:30%;
|
|
margin: 0 10px 0 0;
|
|
background-color: #fff;
|
|
border: 1px solid #ddd;
|
|
border-radius: 5px;
|
|
padding: 10px;
|
|
}
|
|
button {
|
|
background-color: #f50;
|
|
box-shadow: none;
|
|
border: 0;
|
|
border-radius: 5px;
|
|
color: white;
|
|
padding: 5px 10px;
|
|
}
|
|
@media (max-width: 800px) {
|
|
.form-inline input {
|
|
margin: 10px 0;
|
|
max-width:100% !important;
|
|
}
|
|
.form-inline {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<div align="center">
|
|
<form class="form-inline" action="https://app.convertkit.com/forms/1052396/subscriptions" method="post" data-uid="8884355abb" data-format="inline" data-version="5">
|
|
<span> Want a weekly digest of this blog?</span>
|
|
<input name="email_address" type="text" placeholder="Email address" />
|
|
<button type="submit" data-element="submit">Subscribe</button>
|
|
</form>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
<p class="meta">
|
|
|
|
<a class="basic-alignment left" href="https://jvns.ca/blog/2023/07/10/lima--a-nice-way-to-run-linux-vms-on-mac/" title="Previous Post: Lima: a nice way to run Linux VMs on Mac">Lima: a nice way to run Linux VMs on Mac</a>
|
|
|
|
|
|
<a class="basic-alignment right" href="https://jvns.ca/blog/2023/08/03/behind--hello-world/" title="Next Post: Behind "Hello World" on Linux">Behind "Hello World" on Linux</a>
|
|
|
|
</p>
|
|
</footer>
|
|
|
|
</article>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
<nav role="navigation" class="footer-nav"> <a href="/">Archives</a>
|
|
</nav>
|
|
<footer role="contentinfo"><span class="credit">© Julia Evans. </span>
|
|
<span>If you like this, you may like <a href="https://web.archive.org/web/20181228051203/http://www.uliaea.ca/">Ulia Ea</a> or, more seriously, this list of <a href="https://jvns.ca/blogroll">blogs I love</a> or some <a href="https://jvns.ca/bookshelf">books I've read</a>. <br>
|
|
<p class="rc-scout__text"><i class="rc-scout__logo"></i>
|
|
You might also like the <a class="rc-scout__link" href="https://www.recurse.com/scout/click?t=546ea46360584b522270b8c3e5d830f8">Recurse Center</a>, my very favorite programming community <a href="/categories/hackerschool/">(my posts about it)</a></p>
|
|
</span>
|
|
<style class="rc-scout__style" type="text/css">.rc-scout{display:block;padding:0;border:0;margin:0;}.rc-scout__text{display:block;padding:0;border:0;margin:0;height:100%;font-size:100%;}.rc-scout__logo{display:inline-block;padding:0;border:0;margin:0;width:0.85em;height:0.85em;background:no-repeat center url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2012%2015%22%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%2212%22%20height%3D%2210%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2210%22%20height%3D%228%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%222%22%20width%3D%228%22%20height%3D%226%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%2361ae24%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%2361ae24%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%2361ae24%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%2361ae24%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%2361ae24%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%229%22%20width%3D%224%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%2211%22%20width%3D%2210%22%20height%3D%224%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%220%22%20y%3D%2212%22%20width%3D%2212%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%225%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%227%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%228%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%229%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3C%2Fsvg%3E');}.rc-scout__link:link,.rc-scout__link:visited{color:#61ae24;text-decoration:underline;}.rc-scout__link:hover,.rc-scout__link:active{color:#4e8b1d;}</style>
|
|
</footer>
|
|
<script type="text/rocketscript">
|
|
(function(){
|
|
var twitterWidgets = document.createElement('script');
|
|
twitterWidgets.type = 'text/javascript';
|
|
twitterWidgets.async = true;
|
|
twitterWidgets.src = 'http://platform.twitter.com/widgets.js';
|
|
document.getElementsByTagName('head')[0].appendChild(twitterWidgets);
|
|
})();
|
|
</script>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
|