<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Busybox on blog.iankulin.com</title><link>https://blog.iankulin.com/tags/busybox/</link><description>Recent content in Busybox on blog.iankulin.com</description><generator>Hugo</generator><language>en-AU</language><lastBuildDate>Mon, 06 Jan 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.iankulin.com/tags/busybox/index.xml" rel="self" type="application/rss+xml"/><item><title>Perils of Benchmarking</title><link>https://blog.iankulin.com/perils-of-benchmarking/</link><pubDate>Mon, 06 Jan 2025 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/perils-of-benchmarking/</guid><description>&lt;p&gt;I&amp;rsquo;ve been containerising my websites, with their servers to make deployment simple and robust, and to move to a CI/CD workflow. Since an install of a production web server is large, I would be running about ten of these containers, and there&amp;rsquo;s already a good server facing the net and doing the reverse-proxying (NGINX Proxy Manager), I chose to bundle the Busy-Box httpd server with my sites inside the Docker containers.&lt;/p&gt;
&lt;p&gt;I had a vague feeling that there was a performance vs size compromise involved, and during some googling found this &lt;a href="https://github.com/nerkn/nginx-busybox-apache/tree/main"&gt;github repo&lt;/a&gt; where nerkn has bench-marked busy-box vs apache vs nginx with, to me (because of my choice above), alarming results.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-11-16-at-10.37.19-am.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;If NGINX is doing twice the throughput, and is two orders of magnitude quicker, then busy-box is not going to be a good choice for me.&lt;/p&gt;
&lt;p&gt;Before I panicked, I thought I&amp;rsquo;d do my own A/B tests, which since it&amp;rsquo;s containerised is simple. I used the &lt;a href="https://httpd.apache.org/docs/2.4/programs/ab.html"&gt;apache &lt;code&gt;ab&lt;/code&gt; testing tool&lt;/a&gt; - it spits out the basics - times for connecting, processing, and waiting. It does multiple tests and gives you the mean and standard deviation for them. Perfect.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the results for a series of tests. I included a commercial website I suspect is in the same data centre as a sanity check.&lt;/p&gt;
&lt;table class="has-fixed-layout"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Test&lt;/td&gt;&lt;td&gt;Mean time (ms)&lt;/td&gt;&lt;td&gt;St dev&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nextdc.com.au&lt;/td&gt;&lt;td&gt;834&lt;/td&gt;&lt;td&gt;293&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;busy-box uclibc&lt;/td&gt;&lt;td&gt;450&lt;/td&gt;&lt;td&gt;93&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;busy-box uclibc&lt;/td&gt;&lt;td&gt;411&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nginx-alpine&lt;/td&gt;&lt;td&gt;423&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nginx-alpine&lt;/td&gt;&lt;td&gt;410&lt;/td&gt;&lt;td&gt;26&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;busy-box uclibc&lt;/td&gt;&lt;td&gt;398&lt;/td&gt;&lt;td&gt;19&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;busy-box uclibc&lt;/td&gt;&lt;td&gt;419&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nginx-alpine&lt;/td&gt;&lt;td&gt;403&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nginx-alpine&lt;/td&gt;&lt;td&gt;398&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nextdc.com.au&lt;/td&gt;&lt;td&gt;759&lt;/td&gt;&lt;td&gt;306&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Huh. A couple of things jump out. One is that the site is probably fast enough, and the other is that the performance of busy-box and NGINX are similar, like very suspiciously similar. I wonder what happens if I &lt;code&gt;docker compose down&lt;/code&gt; the website and run the test again?&amp;hellip;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;This is ApacheBench, Version 2.3 &amp;lt;$Revision: 1903618 $&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Licensed to The Apache Software Foundation, http://www.apache.org/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Concurrency Level: 10
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Time taken for tests: 4.608 seconds
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Complete requests: 100
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Failed requests: 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Non-2xx responses: 100
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Total transferred: 30300 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTML transferred: 15400 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Requests per second: 21.70 [#/sec] (mean)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Time per request: 460.777 [ms] (mean)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Time per request: 46.078 [ms] (mean, across all concurrent requests)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Transfer rate: 6.42 [Kbytes/sec] received
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Connection Times (ms)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; min mean[+/-sd] median max
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Connect: 274 318 33.6 306 451
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Processing: 82 95 14.5 92 170
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Waiting: 82 95 14.3 92 169
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Total: 362 413 37.5 401 580
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;lol. Okay. I guess I&amp;rsquo;ve been testing the cache in NGINX Proxy Manager this whole time. There is a setting for that, so perhaps I should turn that off. Sadly, even with that turned off, and the container not running, I&amp;rsquo;m still getting that good performance which would be the 500 error coming back from NGINX Proxy Manager.&lt;/p&gt;
&lt;p&gt;Time to trick it into not using the cache by making unique requests each time. I&amp;rsquo;ll use these:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ab -n 100 -c 10 &amp;#34;https://example.com.au/index.html?nocache=$(date +%s%N)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ab -n 100 -c 10 &amp;#34;https://www.nextdc.com/index.html?nocache=$(date%20+%s%N)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;m also able to see that it&amp;rsquo;s hitting the container with all the requests by running the compose up in the foreground and having the logs output. So now I&amp;rsquo;m much more confident about the output. Here&amp;rsquo;s the summary of a much larger group of tests run in that round robin style.&lt;/p&gt;
&lt;table class="has-fixed-layout"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Situation&lt;/td&gt;&lt;td&gt;Mean time (ms)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Nextdc.com&lt;/td&gt;&lt;td&gt;617&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;NGINX Proxy with no site&lt;/td&gt;&lt;td&gt;412&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;NGINX-apline site&lt;/td&gt;&lt;td&gt;420&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Busy-box (uclibc)&lt;/td&gt;&lt;td&gt;424&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The comparison with nextdc is of course unfair. They are returning a lot more html, and some of it could be server rendered. I don&amp;rsquo;t have an explanation of why my results are so different from nerkn&amp;rsquo;s. He&amp;rsquo;s using a different tool, and I imagine on a local network (mine is over a mobile data link, to a VPS in a data centre).&lt;/p&gt;
&lt;p&gt;As far as the container size comparisons go, the NGINX-alpine one is 48.98MB and the uclibc version of BusyBox is 1.35MB. I think I&amp;rsquo;ll be sticking with that.&lt;/p&gt;</description></item><item><title>Fixing TLS for wget in BusyBox</title><link>https://blog.iankulin.com/fixing-tls-for-wget-in-busybox/</link><pubDate>Mon, 25 Nov 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/fixing-tls-for-wget-in-busybox/</guid><description>&lt;p&gt;I&amp;rsquo;ve been containerising my static websites with BusyBox (because it&amp;rsquo;s small), and in &lt;a href="https://blog.iankulin.com/fancier-website-in-a-docker-container/"&gt;an earlier post&lt;/a&gt; showed how to even get the container to update parts of the site by reaching out with &lt;code&gt;wget&lt;/code&gt; to download resources from elsewhere and saving them inside the container where we are serving the &amp;lsquo;static&amp;rsquo; site from. I&amp;rsquo;d done this by including a bash script in the container with the &lt;code&gt;wget&lt;/code&gt; in a loop with a &lt;code&gt;sleep&lt;/code&gt;. Then started the script and the httpd server in the CMD line of the &lt;code&gt;dockerfile&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the dockerfile.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FROM busybox&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;latest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Add shell script and set executable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COPY update_content&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;sh &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;usr&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;local&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;bin&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;update_content&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;sh
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RUN chmod &lt;span style="color:#81a1c1"&gt;+&lt;/span&gt;x &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;usr&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;local&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;bin&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;update_content&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;sh
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Create the directory for the web content, and copy files in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RUN mkdir &lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;p &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;www&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COPY www&lt;span style="color:#81a1c1"&gt;/.&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;www&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Expose port 80 for the web server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;EXPOSE &lt;span style="color:#b48ead"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Start the httpd server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CMD &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;sh&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;-c&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;/usr/local/bin/update_content.sh &amp;amp; busybox httpd -f -p 80 -h /var/www/html&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the bash script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#5e81ac;font-style:italic"&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Define the URL and the destination path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;URL&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;http://httpbin.org/image/jpeg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DEST_PATH&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;/var/www/html/image.jpg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FETCH_INTERVAL&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#b48ead"&gt;120&lt;/span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;# 2 minutes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;while&lt;/span&gt; true&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;# Use wget to download the file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; wget -O &lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;$DEST_PATH&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;$URL&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;# Check the exit status of wget&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;[&lt;/span&gt; $? -eq &lt;span style="color:#b48ead"&gt;0&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;]&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;echo&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;File downloaded successfully to &lt;/span&gt;$DEST_PATH&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;echo&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Failed to download the file.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sleep $FETCH_INTERVAL
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This all works perfectly, as long as the file you&amp;rsquo;re downloading is over http. Trying over an SSL connection (which must be 98% of the internet by now) fails. The reason for this is that &lt;a href="https://blog.iankulin.com/fancier-website-in-a-docker-container/"&gt;BusyBox does not contain the certificates for the root CA&lt;/a&gt;. In a normal distribution, you&amp;rsquo;d just do ahead and install them, but BusyBox also does not have a package manager to help you do that, so there&amp;rsquo;s no &amp;lsquo;&lt;code&gt;apk update &amp;amp;&amp;amp; apk add ca-certificates&lt;/code&gt;&amp;rsquo; to help us out.&lt;/p&gt;
&lt;p&gt;A viable solution might be to just switch to an Alpine container, but I&amp;rsquo;d be going up to 12MB per containerised website then (from 4) which seems a bit much.&lt;/p&gt;
&lt;p&gt;In a &lt;a href="https://blog.iankulin.com/fancier-website-in-a-docker-container/"&gt;Stack Overflow post&lt;/a&gt;, &lt;a href="https://stackoverflow.com/users/2830850/tarun-lalwani"&gt;Tarun Lalwani&lt;/a&gt; offers a couple of suggestions. One is having a multi-stage docker image build where you create an Alpine container, copy the certs out to a volume, then copy them into your busybox image. To my mind that would be a good idea to create a new image (essentially BusyBox with certs) to chuck up on a repository somewhere. Such an &lt;a href="https://hub.docker.com/r/odise/busybox-curl"&gt;image does exist,&lt;/a&gt; but it&amp;rsquo;s very old.&lt;/p&gt;
&lt;p&gt;Another suggestion is just to bind mount the certs directory in the container to the host (as read only), and use the host&amp;rsquo;s certificates. This seems like a much simpler approach to me. It&amp;rsquo;s just an edit to the &lt;code&gt;docker-compose.yml&lt;/code&gt; or the run command.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;services:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; example.com:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; container_name: httpd-example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image: ghcr.io/iankulin/example.com:latest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; restart: unless-stopped
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - /etc/ssl/certs:/etc/ssl/certs:ro # Bind mount host&amp;#39;s SSL certs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;or&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run --name httpd-example.com -p 80:80 -v /etc/ssl/certs:/etc/ssl/certs:ro ghcr.io/iankulin/example.com:latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Fancier Website in a Docker Container</title><link>https://blog.iankulin.com/fancier-website-in-a-docker-container/</link><pubDate>Mon, 18 Nov 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/fancier-website-in-a-docker-container/</guid><description>&lt;p&gt;The previous post went over how to bundle a static website into a Docker container. That&amp;rsquo;s a neat little trick - keeping the entire website and making it trivial to install on a VPS behind Nginx Proxy Manager. It worked great for most of my little websites.&lt;/p&gt;
&lt;h3 id="but"&gt;But&amp;hellip;&lt;/h3&gt;
&lt;p&gt;A couple of my websites had very minor &amp;lsquo;dynamic&amp;rsquo; content. One was pulling down the local temperature from OpenWeather, then exposing a cut-down version of that as a REST endpoint so all my servers could grab it without me being rate-limited by OpenWeather for abusing my free API key. Another one re-hosted an image that changes a couple of times a day from an unreliable service.&lt;/p&gt;
&lt;p&gt;So, can we do those sorts of jobs in our BusyBox web containers? Well yes, of course. Let&amp;rsquo;s look at the image re-hosting problem, but the approach is going to be similar for other small internet tasks.&lt;/p&gt;
&lt;p&gt;We need the container (as well as hosting the website) to repeatedly download an image from the internet, and save it into the directory in the container where the static files are being hosted. In my first attempt at this, I messed around with cron, but I was over complicating it, and since BusyBox is not a full distro with all the regular tools, lots of things (including cron) just didn&amp;rsquo;t work the way I expected the first try.&lt;/p&gt;
&lt;p&gt;Where I ended up was having a script called &lt;code&gt;update_content.sh&lt;/code&gt; that has a loop with a &lt;code&gt;sleep()&lt;/code&gt; in the bottom, but at the top, downloads the file we want into our &lt;code&gt;/var/www/html/&lt;/code&gt; directory. Here&amp;rsquo;s the script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#5e81ac;font-style:italic"&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Define the URL and the destination path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;URL&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;http://httpbin.org/image/jpeg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DEST_PATH&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;/var/www/html/image.jpg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FETCH_INTERVAL&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#b48ead"&gt;120&lt;/span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;# 2 minutes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;while&lt;/span&gt; true&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;# Use wget to download the file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; wget -O &lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;$DEST_PATH&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;$URL&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;# Check the exit status of wget&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;[&lt;/span&gt; $? -eq &lt;span style="color:#b48ead"&gt;0&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;]&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;echo&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;File downloaded successfully to &lt;/span&gt;$DEST_PATH&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;echo&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Failed to download the file.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sleep $FETCH_INTERVAL
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then in our &lt;code&gt;dockerfile&lt;/code&gt;, the &lt;code&gt;CMD&lt;/code&gt; launches the script as well as the httpd server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FROM busybox&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;latest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Add shell script and set executable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COPY update_content&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;sh &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;usr&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;local&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;bin&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;update_content&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;sh
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RUN chmod &lt;span style="color:#81a1c1"&gt;+&lt;/span&gt;x &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;usr&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;local&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;bin&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;update_content&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;sh
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Create the directory for the web content, and copy files in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RUN mkdir &lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;p &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;www&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COPY www&lt;span style="color:#81a1c1"&gt;/.&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;www&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Expose port 80 for the web server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;EXPOSE &lt;span style="color:#b48ead"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Start the httpd server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CMD &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;sh&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;-c&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;/usr/local/bin/update_content.sh &amp;amp; busybox httpd -f -p 80 -h /var/www/html&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The docker-compose.yml remains the same as in the previous post - if you haven&amp;rsquo;t read that, I was running all these website containers behind Nginx Proxy Manager. If you are not, then just go ahead and delete out the &amp;ldquo;networks&amp;rdquo; parts.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;services:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; example.com:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; container_name: httpd-example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image: ghcr.io/iankulin/example.com:latest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; restart: unless-stopped
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; networks:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - nginx-proxy-manager_default
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;networks:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; nginx-proxy-manager_default:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; external: true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Eagle eyed readers (or people with experience of using the BusyBox version of wget) will have noticed the oddly particular image file I chose to download for this demo code:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;URL=&amp;quot;http://httpbin.org/image/jpeg&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I chose this, because it&amp;rsquo;s one of the last image files in the world to be served over http, and wget in BusyBox chokes on TLS for reasons I&amp;rsquo;ll discuss next week.&lt;/p&gt;</description></item><item><title>Website in a Docker Container</title><link>https://blog.iankulin.com/website-in-a-docker-container/</link><pubDate>Mon, 11 Nov 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/website-in-a-docker-container/</guid><description>&lt;p&gt;Having figured out how to use the GitHub package registry, I was a bit inspired by &lt;a href="https://lipanski.com/posts/smallest-docker-image-static-website"&gt;this blog post&lt;/a&gt; from Florin Lipan to deliver all my little static websites as Docker containers. I&amp;rsquo;m not as focused as he is about making them tiny, but I did steal the idea of using &lt;a href="https://busybox.net/about.html"&gt;BusyBox&lt;/a&gt; httpd for serving them, resulting in about 4MB containers. That&amp;rsquo;s small enough for me, and since they are all very similar, there&amp;rsquo;s a fair bit of layer reuse going on.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the setup. I dump the static (html, css, js etc) files for the website into a &amp;lsquo;www&amp;rsquo; sub-directory.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-10-19-at-6.55.02-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-10-19-at-6.55.02-pm.png" width="1000" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The dockerfile pulls in BusyBox then copies those files into the container. Note these are in the container, it&amp;rsquo;s not going to be bound to an external directory (where we could change them), the container carries its website files with it. Any change to the website content will require a container rebuild.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;EXPOSE 80&lt;/code&gt; doesn&amp;rsquo;t really do anything, it&amp;rsquo;s pretty much just documentation. Then the &lt;code&gt;CMD&lt;/code&gt; directive starts the server on port 80 and points to the static files that we copied in earlier.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FROM busybox&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;latest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Create the directory for the web content, and copy files in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RUN mkdir &lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;p &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;www&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COPY www&lt;span style="color:#81a1c1"&gt;/.&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;www&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Expose port 80 for the web server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;EXPOSE &lt;span style="color:#b48ead"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;# Start the httpd server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CMD &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;sh&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;-c&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;busybox httpd -f -p 80 -h /var/www/html&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To use this dockerfile to build our container, just docker build it and give it a tag:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker build -t ghcr.io/iankulin/example.com:latest .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then if we run it, and go to http://localhost, there&amp;rsquo;s our website.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run --name httpd-example.com -p 80:80 ghcr.io/iankulin/example.com:latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-10-19-at-7.15.11-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-10-19-at-7.15.11-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="with-nginx-proxy-manager"&gt;With NGINX Proxy Manager&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;docker-compose.yml&lt;/code&gt; file I use on the VPS host is slightly more complicated. We want each of the website containers to run in the same docker network as Nginx Proxy Manager - since docker networks have their own little dns server based on the container names, that&amp;rsquo;s going to make hooking it up trivial.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;services:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; example.com:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; container_name: httpd-example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image: ghcr.io/iankulin/example.com:latest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; restart: unless-stopped
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; networks:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - nginx-proxy-manager_default
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;networks:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; nginx-proxy-manager_default:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; external: true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="architecture"&gt;Architecture&lt;/h3&gt;
&lt;p&gt;Since I develop on an M1 MacBook, but host all my workloads on regular AMD64 Linux LXC containers or VMs, I need to build for that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker build --platform linux/amd64 -t ghcr.io/iankulin/example.com:latest .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In actual fact, I could have built that way for the Mac as well - Docker Desktop would have just run it in a Linux VM with a small performance penalty which wouldn&amp;rsquo;t be noticeable for my purposes. Once it&amp;rsquo;s built, we push it up to the GitHub Container Registry.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker push ghcr.io/iankulin/example.com:latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Working with the registry is well covered in my previous post, so I won&amp;rsquo;t go into those details here.&lt;/p&gt;
&lt;h3 id="on-the-host"&gt;On the host&lt;/h3&gt;
&lt;p&gt;On the host where the website is to run, I just make a directory for it and drop the &lt;code&gt;docker-compose.yml&lt;/code&gt; in. Then &lt;code&gt;docker compose up -d&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-10-19-at-7.46.31-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-10-19-at-7.46.31-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Since we&amp;rsquo;re running in the Nginx Proxy Manager docker network, when we specify the host name for the new web site for NPM to proxy to, it&amp;rsquo;s just the container name we gave it.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-10-19-at-8.06.44-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-10-19-at-8.06.44-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then the DNS settings for your domain need to be pointed to this host. Once that&amp;rsquo;s propagated, you&amp;rsquo;ll be able to request the SSL certificate in NPM and your website is live.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-10-19-at-8.11.23-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-10-19-at-8.11.23-pm.png" width="886" alt=""&gt;&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>