<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Docker Archives - Karnavaara</title>
	<atom:link href="https://karnavaara.com/tag/docker/feed" rel="self" type="application/rss+xml" />
	<link>https://karnavaara.com/tag/docker</link>
	<description>The Forested Hill of Insight</description>
	<lastBuildDate>Mon, 09 Jun 2025 06:53:56 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://karnavaara.com/wp-content/uploads/2023/10/cropped-Karnavaara-favicon-DarkGreen-32x32.png</url>
	<title>Docker Archives - Karnavaara</title>
	<link>https://karnavaara.com/tag/docker</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>My Path To Self-Hosting WordPress Powerfully for Only Pennies</title>
		<link>https://karnavaara.com/genesis/self-hosting-wordpress-for-pennies</link>
		
		<dc:creator><![CDATA[Kvaara]]></dc:creator>
		<pubDate>Mon, 23 Oct 2023 01:06:03 +0000</pubDate>
				<category><![CDATA[Genesis]]></category>
		<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[CMS]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[HTTPS]]></category>
		<category><![CDATA[Reverse Proxy]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://karnavaara.com/?p=1351</guid>

					<description><![CDATA[A penny for your... WordPress? Powerfully and cost-effectively self-hosting WordPress with SSL.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><em>Updated on 9.6.2025: Small amends such as reducing the amount of &#8220;speaking in absolutes&#8221; and fixing links so that they open in new tabs.</em><br><em><a href="#update1">Updated on 20.12.2024</a>: I added another useful plugin called WPS Hide Login to change the default WordPress login URL as an additional security layer combined with Limit Login Attempts Reloaded.</em></p>



<h2 class="wp-block-heading" id="frame-of-reference">Frame of Reference</h2>



<p class="wp-block-paragraph">Today, in the 21st century, it&#8217;s not difficult at all to create your own websites. In the majority of cases, it&#8217;s not even expensive. <em>Everybody can do it</em>! The real difficulty lies in how you go about doing it. For example, consider this table:</p>



<div style="height:0px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-table is-style-regular"><table class="has-white-background-color has-background"><thead><tr><th>EASY</th><th>NORMAL</th><th>HARD</th></tr></thead><tbody><tr><td>Plug and Play <em><a href="https://www.optimizely.com/optimization-glossary/content-management-system/" target="_blank" rel="noreferrer noopener">Content Management Systems</a></em> (e.g., Medium, Substack, Squarespace, and WordPress.com)</td><td><a href="https://wordpress.org/" target="_blank" rel="noreferrer noopener">WordPress.org</a> (self-hosted)</td><td><em><a href="https://www.w3.org/wiki/The_web_standards_model_-_HTML_CSS_and_JavaScript" target="_blank" rel="noreferrer noopener">Vanilla</a></em> (CSS/HTML/JS)</td></tr><tr><td>Buying a personalized solution from <a href="https://fiverr.com/" target="_blank" rel="noreferrer noopener">Fiverr</a>.</td><td><em><a href="https://www.cloudflare.com/en-gb/learning/performance/static-site-generator/" target="_blank" rel="noreferrer noopener">Static Site Generators</a></em> (e.g., Hugo and Jekyll)</td><td><a href="https://airfocus.com/glossary/what-is-a-front-end/" target="_blank" rel="noreferrer noopener"><em>Front end</em></a> (e.g., React) with or without a <a href="https://techterms.com/definition/backend" target="_blank" rel="noreferrer noopener"><em>back end</em></a> (e.g., NodeJS)</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">This table should provide a very basic <em><a href="https://en.wikipedia.org/wiki/Frame_of_reference" target="_blank" rel="noreferrer noopener">frame of reference</a></em> for choosing the path of least (or most) resistance. For example, the easiest way for you to erect your own blogging site is through <a href="https://medium.com/" target="_blank" rel="noreferrer noopener">Medium</a>: Just sign up with Google (<a href="https://www.wired.com/story/single-sign-on-facebook-google-apple/" target="_blank" rel="noreferrer noopener"><em>or maybe don&#8217;t</em></a>) and click the &#8220;Write&#8221; button. Substack is very similar.<a href="https://karnavaara.com/genesis/self-hosting-wordpress-for-pennies" target="_blank" rel="noreferrer noopener"></a></p>



<p class="wp-block-paragraph">Want more personalization options while preserving simplicity? <a href="https://wordpress.com/">WordPress.com</a> and <a href="https://www.squarespace.com/">Squarespace</a> are your friends. Both can be personalized to fit your needs uniquely. They don&#8217;t have that basic Medium or <a href="https://substack.com/">Substack </a><em>look.</em></p>



<p class="wp-block-paragraph">People who are tech-savvy, tinkerers, <a href="https://en.wikipedia.org/wiki/Self-hosting_(web_services)#Benefits"><em>concerned with privacy</em></a>, and/or have more time on their hands should look for a self-hosted option like WordPress.org, <a href="https://gohugo.io/">Hugo</a><sup data-fn="019886f8-0e44-4fff-9bad-0ae7b9b42206" class="fn"><a id="019886f8-0e44-4fff-9bad-0ae7b9b42206-link" href="#019886f8-0e44-4fff-9bad-0ae7b9b42206">1</a></sup>, or perhaps endeavor into an entire <a href="https://www.linkedin.com/pulse/step-by-step-guide-setting-up-full-stack-web-python-swarali-garud/"><em>full stack implementation</em></a>.</p>



<h2 class="wp-block-heading" id="the-elephant-in-the-room">The Elephant in the Room</h2>



<p class="wp-block-paragraph">Pricing. Sure, those 3rd party hosted solutions like WordPress.com, Medium, Substack, and Squarespace are simple and the <a href="https://enkonix.com/blog/time-to-market/"><em>time to market</em></a> (TTM) is very fast. But they&#8217;re not entirely free (at least in the personalization department). Check out this new and revolutionary table:</p>



<figure class="wp-block-table"><table class="has-white-background-color has-background"><thead><tr><th>PRODUCT</th><th>COST</th></tr></thead><tbody><tr><td>Medium</td><td><strong>Writing and publishing blog posts is entirely free</strong> but collaborative publications (think of these like magazines)  and custom domains (e.g., karnavaara.com) are only available for paid members ($5/month).</td></tr><tr><td>Squarespace</td><td>Minimum cost is <strong>11€/month</strong> (that is, if it&#8217;s paid annually)</td></tr><tr><td>Substack</td><td><strong>Everything is free for writers</strong>. However, you have to pay a one-time fee of $50 for the ability to use a custom domain.</td></tr><tr><td>WordPress.com</td><td><strong>Basic usage (e.g., writing and publishing posts) is entirely free</strong>. 4€/month will enable the use of custom domains. Personal branding will require custom styles, which will cost 8€/month. Having the ability to use plugins (which is arguably the most important feature) will cost 25€/month. </td></tr></tbody></table><figcaption class="wp-element-caption">This table revolves mainly around using these products as blogging sites.  </figcaption></figure>



<p class="wp-block-paragraph">So, <strong>if you want to get your posts to the market hastily and cost-effectively, and you don&#8217;t care about personal branding, I recommend Medium or Substack.</strong> If you&#8217;re like me and desire personal branding, the choice would be between Squarespace and WordPress.com. </p>



<h2 class="wp-block-heading" id="my-path">My Path</h2>



<h3 class="wp-block-heading">Static Site Generators</h3>



<p class="wp-block-paragraph">The simplicity and aesthetic of <em>Static Site Generators</em> (SSG) like Hugo took me by storm. If you&#8217;ve even slightly dabbled in web development, setting up a full-fledged Hugo blog with a ready-to-use theme will take only an hour (if even that).<sup data-fn="448b6a3e-a196-46cd-a51a-8a9fb6717667" class="fn"><a id="448b6a3e-a196-46cd-a51a-8a9fb6717667-link" href="#448b6a3e-a196-46cd-a51a-8a9fb6717667">2</a></sup> At the other side of the spectrum is full stack development but <em>sadly</em> I don&#8217;t have the time to pursue that endeavor.</p>



<p class="wp-block-paragraph">Unfortunately&#8230; I won that storm and decided to join the battle between Squarespace and 3rd party hosted WordPress. <em>Why?</em> Well, first of all, out of curiosity, and last of all, for laziness. I got an epiphany and WordPress just happened to have the <em>perfect theme</em>.</p>



<h3 class="wp-block-heading">The discovery of perfection-ish.</h3>



<p class="wp-block-paragraph">Even though I found &#8220;<a href="https://wordpress.com/theme/livro?style_variation=cream">perfection</a>&#8221; in WordPress, the choice between it and Squarespace was somewhat difficult. Squarespace provides <a href="https://www.optimizely.com/optimization-glossary/search-engine-optimization/"><em>Search Engine Optimization</em></a> (SEO) straight out of the box while in WordPress you can only achieve the same with <a href="https://blog.hubspot.com/website/wordpress-plugins#what-is-a-WordPress-plugin"><em>plugins</em></a>. Additionally, Squarespace has unlimited storage, which none of the WordPress plans have.</p>



<p class="wp-block-paragraph">My choice boiled down to Squarespace&#8217;s Business plan (17€/month annually) and WordPress&#8217;s Business (24€/month annually). The Squarespace business plan would give me <em><a href="https://www.squarespace.com/pricing">complete customization with CSS and JavaScript</a></em>, SEO, and unlimited storage. The WordPress business plan would enable plugins (e.g., <a href="https://wordpress.org/plugins/wordpress-seo/">Yoast SEO</a>), which have a plethora of use cases.</p>



<h3 class="wp-block-heading">Ultimatum</h3>



<p class="wp-block-paragraph">I really wanted the plugins&#8230; But 24€/month for my own personal blog<sup data-fn="e4e1333e-3fa7-458e-95b1-93d1fec2a166" class="fn"><a id="e4e1333e-3fa7-458e-95b1-93d1fec2a166-link" href="#e4e1333e-3fa7-458e-95b1-93d1fec2a166">3</a></sup>, which doesn&#8217;t generate any direct revenue (<em>though I believe it does indirectly</em>). Then again, going with Squarespace and saving 7€/month isn&#8217;t the saving grace. WordPress won, but with an ultimatum: We use the entirely free and self-hosted version of WordPress.</p>



<p class="wp-block-paragraph">Why the ultimatum? <strong>I had an idea how to get the business version of WordPress for only pennies.</strong> Additionally, I have played around with <a href="https://www.youtube.com/watch?v=Gjnup-PuquQ&amp;ab_channel=Fireship"><em>Docker</em></a>, Linux, different <em><a href="https://cloud.google.com/learn/what-is-a-cloud-service-provider">Cloud Service Providers</a> </em>(CSPs), and Networking so this wasn&#8217;t going to be a big undertaking.</p>



<h2 class="wp-block-heading" id="how">How</h2>



<p class="wp-block-paragraph">First, come up with a <em><a href="https://www.wrike.com/blog/what-is-an-action-plan-with-example/">plan of action</a></em>. However, to do that, you must have a relatively good <em>bird&#8217;s eye view</em> on a top level, which only develops through experience. Here&#8217;s a very bare-bones representation of one, although more akin to a list of actions:</p>



<ol style="list-style-type:upper-roman" class="wp-block-list">
<li>Choose the most cost-effective, performant, and secure CSP.
<ul class="wp-block-list">
<li><s>Hetzner Cloud?</s> <sub>Although cheap, the <a href="https://www.hetzner.com/cloud#pricing"><em>VMs aren&#8217;t free</em></a>.</sub></li>



<li><s>Amazon Web Services?</s> <a href="https://aws.amazon.com/ec2/pricing/#AWS_Free_Tier"><em><sub>VMs are only free up to a point.</sub></em></a></li>



<li><s>Azure?</s> <a href="https://azure.microsoft.com/en-us/pricing/free-services"><em><sub>Similar to AWS.</sub></em></a></li>



<li>Google Cloud Provider (GCP)? There&#8217;s an<em> <a href="https://cloud.google.com/free/docs/free-cloud-features#compute">always-free instance in certain regions</a> </em>but it&#8217;s<em> <a href="https://cloud.google.com/compute/docs/general-purpose-machines#e2-shared-core">very weak</a>.</em></li>



<li>Oracle Cloud Infrastructure (OCI)? There are <em><a href="https://docs.oracle.com/en-us/iaas/Content/FreeTier/freetier_topic-Always_Free_Resources.htm#ariaid-title4">multiple always-free shapes </a></em>for instances (one of which can be very powerful).</li>
</ul>
</li>



<li>Provision a <em><a href="https://aws.amazon.com/what-is/cloud-instances/">compute instance</a></em> with a free shape and with valid networking.  </li>



<li>Install and configure a self-hosted WordPress instance to the compute instance.
<ul class="wp-block-list">
<li><s>Setting it up all vanilla style with a <a href="https://www.vmware.com/nordics/topics/glossary/content/virtual-machine.html"><em>Virtual Machine</em></a> (VM) without using a <a href="https://www.aquasec.com/cloud-native-academy/container-security/container-runtime/"><em>Container Runtime</em></a> (e.g., Docker)?</s> <sub>Blasphemy&#8230; Just kidding <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> This would be too much of an overkill</sub><sup data-fn="97810694-81d6-4b55-b75c-88bf796dc46b" class="fn"><a id="97810694-81d6-4b55-b75c-88bf796dc46b-link" href="#97810694-81d6-4b55-b75c-88bf796dc46b">4</a></sup></li>



<li>Using preconfigured and prebuilt WordPress Docker Images<sup data-fn="fe6fded5-9050-4c17-8de9-95466bd380f6" class="fn"><a id="fe6fded5-9050-4c17-8de9-95466bd380f6-link" href="#fe6fded5-9050-4c17-8de9-95466bd380f6">5</a></sup>?</li>
</ul>
</li>



<li>Configure TLS/SSL Encryption (i.e., HTTPS).</li>



<li>Go through the famous <a href="https://developer.wordpress.org/advanced-administration/before-install/howto-install/#finishing-installation"><em>five-minute WordPress installation process</em></a>.</li>



<li>Install plugins for security hardening, faster performance, and resilience.</li>
</ol>



<p class="wp-block-paragraph">This list is very high level and is probably abstract for someone inexperienced. But I&#8217;ll go from abstract to concrete in detail. So, let&#8217;s get started!</p>



<h3 class="wp-block-heading" id="choose-the-most-cost-effective-performant-and-secure-csp">Choose the most cost-effective, performant, and secure CSP</h3>



<p class="wp-block-paragraph">As you might have picked up from the action plan, there are really two choices: Google Cloud Provider or Oracle Cloud Infrastructure. If you went through the links, you&#8217;d conclude that OCI&#8217;s the clear winner here:</p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29a83d&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29a83d" class="wp-block-image size-full has-custom-border wp-lightbox-container"><img fetchpriority="high" decoding="async" width="956" height="519" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-1.png" alt="As far as I know, no other Cloud Provider as established as Oracle can beat this." class="has-border-color wp-image-1444" style="border-color:#aaaaaa;border-width:1px;border-radius:4px" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-1.png 956w, https://karnavaara.com/wp-content/uploads/2023/10/image-1-300x163.png 300w, https://karnavaara.com/wp-content/uploads/2023/10/image-1-768x417.png 768w" sizes="(max-width: 956px) 100vw, 956px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">As far as I know, no other Cloud Provider as established as Oracle can beat this.</figcaption></figure>



<p class="wp-block-paragraph">The perspicacious of you will notice that this is a compute instance with an <a href="https://www.techtarget.com/whatis/definition/ARM-processor"><em>Arm processor</em></a> instead of an AMD one. For our use case, this won&#8217;t be a problem as Docker can be installed on Arm Linux distributions and there are <a href="https://hub.docker.com/r/arm64v8"><em>Arm compatible Docker images</em></a> for WordPress and <a href="https://www.oracle.com/mysql/what-is-mysql/"><em>MySQL</em></a>. <strong>Especially for the cloud, I think Arm processors will be the future. </strong></p>



<h3 class="wp-block-heading" id="provision-the-compute-instance-with-a-free-shape-and-with-valid-networking-on-that-chosen-csp">Provision a compute instance with a free shape and with valid networking</h3>



<h4 class="wp-block-heading">Compute instance </h4>



<p class="wp-block-paragraph">If you haven&#8217;t already, you need to create an OCI account. This will require a credit card as Oracle wants to confirm your identity and prevent the misuse of their cloud.</p>



<p class="wp-block-paragraph">After creating your account and logging into OCI, do these steps below: </p>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-37ae2f39 wp-block-group-is-layout-flex" style="border-radius:0px;padding-top:0;padding-right:0;padding-bottom:0;padding-left:0">
<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29af9c&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29af9c" class="wp-block-image size-thumbnail wp-lightbox-container"><img decoding="async" width="150" height="150" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-2-150x150.png" alt="1. Go to the &quot;Instances&quot; page." class="wp-image-1455"/><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">1. Go to the &#8220;Instances&#8221; page.</figcaption></figure>



<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29b446&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29b446" class="wp-block-image size-thumbnail wp-lightbox-container"><img decoding="async" width="150" height="150" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-3-150x150.png" alt="2. Click &quot;Create instance&quot;." class="wp-image-1456"/><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">2. Click &#8220;Create instance&#8221;.</figcaption></figure>



<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29b937&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29b937" class="wp-block-image size-medium wp-lightbox-container"><img loading="lazy" decoding="async" width="300" height="127" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-4-300x127.png" alt="3. Select the correct image and shape." class="wp-image-1457" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-4-300x127.png 300w, https://karnavaara.com/wp-content/uploads/2023/10/image-4-1024x434.png 1024w, https://karnavaara.com/wp-content/uploads/2023/10/image-4-768x326.png 768w, https://karnavaara.com/wp-content/uploads/2023/10/image-4.png 1276w" sizes="auto, (max-width: 300px) 100vw, 300px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">3. Select the correct image and shape.</figcaption></figure>



<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29be0d&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29be0d" class="wp-block-image size-medium wp-lightbox-container"><img loading="lazy" decoding="async" width="300" height="137" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-5-300x137.png" alt="4. Create a VCN with a public subnet." class="wp-image-1458" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-5-300x137.png 300w, https://karnavaara.com/wp-content/uploads/2023/10/image-5-1024x466.png 1024w, https://karnavaara.com/wp-content/uploads/2023/10/image-5-768x349.png 768w, https://karnavaara.com/wp-content/uploads/2023/10/image-5.png 1323w" sizes="auto, (max-width: 300px) 100vw, 300px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">4. Create a VCN with a public subnet.</figcaption></figure>



<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29c2fb&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29c2fb" class="wp-block-image size-medium wp-lightbox-container"><img loading="lazy" decoding="async" width="300" height="79" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-6-300x79.png" alt="5. Save the generated private SSH key." class="wp-image-1459" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-6-300x79.png 300w, https://karnavaara.com/wp-content/uploads/2023/10/image-6-1024x271.png 1024w, https://karnavaara.com/wp-content/uploads/2023/10/image-6-768x203.png 768w, https://karnavaara.com/wp-content/uploads/2023/10/image-6.png 1247w" sizes="auto, (max-width: 300px) 100vw, 300px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">5. Save the generated private SSH key.</figcaption></figure>
</div>



<div class="wp-block-group is-layout-constrained wp-block-group-is-layout-constrained">
<details class="wp-block-details is-layout-flow wp-block-details-is-layout-flow"><summary>A pro tip regarding the SSH key&#8230;</summary>
<p class="wp-block-paragraph" id="ssh-key-pro-tip">In my opinion, it&#8217;s a major mistake to save the SSH key locally <em>without any encryption</em>. You should use a centralized <a href="https://en.wikipedia.org/wiki/Password_manager">Password Manager</a> like <a href="https://keepass.info/">KeePass</a>, <a href="https://www.lastpass.com/">LastPass</a>, or <a href="https://1password.com/">1Password</a>. I personally use 1Password as their <a href="https://support.1password.com/1password-security/">security model is top-notch</a>.</p>



<p class="wp-block-paragraph">With 1Password you can effortlessly and pleasingly save your private SSH keys as seen in the picture below. You can even create your own taxonomy by using tags and categories.</p>



<div class="wp-block-group is-layout-constrained wp-block-group-is-layout-constrained">
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="758" height="766" fetchpriority="low" src="https://karnavaara.com/wp-content/uploads/2023/10/image-17.png" alt="Picture showing how you can effortlessly and pleasingly save private SSH keys. " class="wp-image-1806" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-17.png 758w, https://karnavaara.com/wp-content/uploads/2023/10/image-17-297x300.png 297w" sizes="auto, (max-width: 758px) 100vw, 758px" /><figcaption class="wp-element-caption">Picture showing how you can effortlessly and pleasingly save private SSH keys.</figcaption></figure>



<p class="wp-block-paragraph">Afterward, in the 1Password Windows application, you can set up the&nbsp;<a href="https://developer.1password.com/docs/ssh/agent/" target="_blank" rel="noreferrer noopener">1Password SSH Agent</a>, which you can use in your Git and SSH workflows. What makes this feature brilliant is that it won&#8217;t leak your private SSH keys outside the application during usage:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="801" height="521" fetchpriority="low" src="https://karnavaara.com/wp-content/uploads/2023/10/image-18.png" alt="In the 1Password Windows application's settings, you can enable a feature called 1Password SSH Agent." class="wp-image-1892" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-18.png 801w, https://karnavaara.com/wp-content/uploads/2023/10/image-18-300x195.png 300w, https://karnavaara.com/wp-content/uploads/2023/10/image-18-768x500.png 768w" sizes="auto, (max-width: 801px) 100vw, 801px" /><figcaption class="wp-element-caption">In the 1Password Windows application&#8217;s settings, you can enable a feature called 1Password SSH Agent.</figcaption></figure>
</div>
</details>



<p class="wp-block-paragraph">Before you create the instance by clicking the button at the bottom, take a look at the &#8220;Summary&#8221; panel situated on the bottom right.</p>



<h4 class="wp-block-heading">Where the Pennies Come From</h4>



<div class="wp-block-group is-layout-constrained wp-container-core-group-is-layout-a6a2a725 wp-block-group-is-layout-constrained">
<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29d1c8&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29d1c8" class="wp-block-image alignright size-medium is-resized wp-lightbox-container"><img loading="lazy" decoding="async" width="300" height="216" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-8-300x216.png" alt="Image showing the cost of the compute instance." class="wp-image-1473" style="width:242px;height:auto" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-8-300x216.png 300w, https://karnavaara.com/wp-content/uploads/2023/10/image-8.png 342w" sizes="auto, (max-width: 300px) 100vw, 300px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">Not that expensive, right?</figcaption></figure>



<p class="wp-block-paragraph" id="mandatory-costs">This is the reason why the title of this post doesn&#8217;t have the words &#8220;For Free&#8221; but &#8220;For Pennies&#8221;. The shape itself will be free even if it&#8217;s running 24/7/365. The only costs will be from storage (boot volume in this context) and <em><a href="https://www.cloudflare.com/en-gb/learning/cloud/what-are-data-egress-fees/">data egress</a></em> leaving the <a href="https://www.oracle.com/cloud/networking/virtual-cloud-network/"><em>Virtual Cloud Network</em></a> (VCN). <strong>Although, in practice, you will probably only pay for using storage as the <a href="https://www.oracle.com/cloud/pricing/">first 10 TB of data egress in OCI is free per month</a>. </strong></p>
</div>
</div>



<p class="wp-block-paragraph">You can now create the instance. After creating it, you must edit the VCN&#8217;s <a href="https://docs.oracle.com/en-us/iaas/Content/Network/Concepts/securitylists.htm"><em>Security Lists</em></a> and add a rule to allow ingress traffic to the instance. Ingress is, by default, blocked in OCI <a href="https://docs.oracle.com/en-us/iaas/Content/Network/Concepts/securitylists.htm#ariaid-title4"><em>with a few exceptions</em></a>. </p>



<h4 class="wp-block-heading">Allowing HTTPS traffic</h4>



<p class="wp-block-paragraph">Here&#8217;s another set of steps to do:</p>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-f56f613f wp-block-group-is-layout-flex">
<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29d908&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29d908" class="wp-block-image size-thumbnail wp-lightbox-container"><img loading="lazy" decoding="async" width="150" height="150" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-9-150x150.png" alt="1. Navigate to &quot;Virtual Cloud Networks&quot;." class="wp-image-1481"/><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">1. Navigate to &#8220;Virtual Cloud Networks&#8221;.</figcaption></figure>



<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29dde1&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29dde1" class="wp-block-image size-thumbnail wp-lightbox-container"><img loading="lazy" decoding="async" width="150" height="150" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-11-150x150.png" alt="2. Click the VCN you created" class="wp-image-1483"/><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">2. Click the VCN you created</figcaption></figure>



<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29e290&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29e290" class="wp-block-image size-medium wp-lightbox-container"><img loading="lazy" decoding="async" width="300" height="239" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-12-300x239.png" alt="3. Go to the VCN's default Security List." class="wp-image-1484" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-12-300x239.png 300w, https://karnavaara.com/wp-content/uploads/2023/10/image-12-768x612.png 768w, https://karnavaara.com/wp-content/uploads/2023/10/image-12.png 907w" sizes="auto, (max-width: 300px) 100vw, 300px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">3. Go to the VCN&#8217;s default Security List.</figcaption></figure>



<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29e718&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29e718" class="wp-block-image size-medium is-resized wp-lightbox-container"><img loading="lazy" decoding="async" width="300" height="140" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-13-300x140.png" alt="4. Add a rule allowing HTTPS." class="wp-image-1485" style="width:181px;height:auto" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-13-300x140.png 300w, https://karnavaara.com/wp-content/uploads/2023/10/image-13-1024x479.png 1024w, https://karnavaara.com/wp-content/uploads/2023/10/image-13-768x359.png 768w, https://karnavaara.com/wp-content/uploads/2023/10/image-13-1536x719.png 1536w, https://karnavaara.com/wp-content/uploads/2023/10/image-13.png 1917w" sizes="auto, (max-width: 300px) 100vw, 300px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">4. Add a rule allowing HTTPS.</figcaption></figure>
</div>



<p class="wp-block-paragraph">If you don&#8217;t want to configure <a href="https://aws.amazon.com/what-is/ssl-certificate/"><em>TLS/SSL</em></a> for your WordPress server, you need to add a rule allowing HTTP (TCP port 80).<strong> However, it&#8217;s of utmost importance you set up TLS/SSL.</strong> If you don&#8217;t, the traffic will be unencrypted and everybody visiting your WordPress site will see it as unsecure. I&#8217;ll show you how you can do this.</p>



<h3 class="wp-block-heading" id="install-and-configure-the-self-hosted-wordpress-instance-to-the-preceding-compute-instance">Install and configure a self-hosted WordPress instance to the compute instance.</h3>



<h4 class="wp-block-heading">Why Docker?</h4>



<p class="wp-block-paragraph">As mentioned in the plan of action, you can do this with or without Docker. However, I&#8217;m going to use Docker as the <em><a href="https://sysdig.com/learn-cloud-native/container-security/what-are-container-runtimes/">container runtime</a></em> for the <em>simplicity</em>, <em>familiarity</em>, and <em>orchestration</em> it provides.</p>



<p class="wp-block-paragraph"><em><a href="https://www.ucl.ac.uk/isd/what-ssh-and-how-do-i-use-it">SSH</a></em> into the provisioned compute instance with the private key you saved. If you&#8217;re on Windows you can do this via <a href="https://www.putty.org/">PuTTY</a> or <em><a href="https://learn.microsoft.com/en-us/windows/wsl/install">Windows Subsystem for Linux</a></em> (WSL). I recommend WSL as it enables you to use <a href="https://www.openssh.com/">OpenSSH</a> (the de facto SSH protocol). Via SSH, install Docker Engine by following <a href="https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository">the instructions found on Docker docs</a>. </p>



<p class="wp-block-paragraph">A great thing about Docker is that it comes with its own native container orchestration tool called <a href="https://docs.docker.com/compose/">Docker Compose</a>, which is much simpler and more lightweight than <a href="https://kubernetes.io/">Kubernetes</a>. We&#8217;re going to use Docker Compose to install and configure WordPress.</p>



<h4 class="wp-block-heading">Docker Composing WordPress</h4>



<p class="wp-block-paragraph">Create a file called <em><a href="https://docs.docker.com/compose/compose-file/03-compose-file/">docker-compose.yml</a></em> using a Command Line Text Editor like Nano or Vim:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">Bash</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>nano docker-compose.yml</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #FFCB6B">nano</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">docker-compose.yml</span></span></code></pre></div>



<p class="wp-block-paragraph">Add the following contents to it <strong>remembering to change the placeholders properly</strong>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">YAML</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>version: &#8216;3.8&#8217;

services:
  wordpress:
    image: arm64v8/wordpress
    restart: always
    ports:
      &#8211; 443:443
      &#8211; 80:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: &lt;DB_USER_HERE>
      WORDPRESS_DB_PASSWORD: &lt;DB_PASSWORD_HERE>
      WORDPRESS_DB_NAME: &lt;DB_NAME_HERE>
    volumes:
      &#8211; wordpress:/var/www/html
  db:
    image: arm64v8/mysql
    restart: always
    environment:
      MYSQL_DATABASE: &lt;DB_NAME_HERE>
      MYSQL_USER: &lt;DB_USER_HERE>
      MYSQL_PASSWORD: &lt;DB_PASSWORD_HERE>
      MYSQL_RANDOM_ROOT_PASSWORD: &#8216;1&#8217;
    volumes:
      &#8211; db:/var/lib/mysql

volumes:
  wordpress:
  db:</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #F07178">version</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&#39;</span><span style="color: #C3E88D">3.8</span><span style="color: #89DDFF">&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F07178">services</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #F07178">wordpress</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">image</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">arm64v8/wordpress</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">restart</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">always</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">ports</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">443:443</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">80:80</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">environment</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">WORDPRESS_DB_HOST</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">db</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">WORDPRESS_DB_USER</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_USER_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">WORDPRESS_DB_PASSWORD</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_PASSWORD_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">WORDPRESS_DB_NAME</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_NAME_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">volumes</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">wordpress:/var/www/html</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #F07178">db</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">image</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">arm64v8/mysql</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">restart</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">always</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">environment</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">MYSQL_DATABASE</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_NAME_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">MYSQL_USER</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_USER_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">MYSQL_PASSWORD</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_PASSWORD_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">MYSQL_RANDOM_ROOT_PASSWORD</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&#39;</span><span style="color: #C3E88D">1</span><span style="color: #89DDFF">&#39;</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">volumes</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">db:/var/lib/mysql</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F07178">volumes</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #F07178">wordpress</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #F07178">db</span><span style="color: #89DDFF">:</span></span></code></pre></div>



<p class="wp-block-paragraph">As you can see, we will compose two containers: WordPress and a MySQL database. Both of them are going to be based on the <em><a href="https://hub.docker.com/r/arm64v8">arm64v8</a></em> architecture. <em>Why MySQL?</em> Because a database is <a href="https://solidwp.com/blog/wordpress-database-explained/#h-why-does-wordpress-need-a-database"><em>a hard requirement for WordPress</em></a>. Without a relational database like MySQL or <a href="https://www.postgresql.org/">PostgreSQL</a>, there would be no WordPress.</p>



<p class="wp-block-paragraph">Execute the command below to start up the above Docker Compose manifest:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">1. Start the containers as defined in the manifest:</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>docker compose up</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #FFCB6B">docker</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">compose</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">up</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">2. Confirm they&#8217;re running:</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>sudo docker container list</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #FFCB6B">sudo</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">docker</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">container</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">list</span></span></code></pre></div>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-f56f613f wp-block-group-is-layout-flex">
<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29f04d&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29f04d" class="wp-block-image size-full wp-lightbox-container"><img loading="lazy" decoding="async" width="345" height="115" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-14.png" alt="You should see a similar output." class="wp-image-1515" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-14.png 345w, https://karnavaara.com/wp-content/uploads/2023/10/image-14-300x100.png 300w" sizes="auto, (max-width: 345px) 100vw, 345px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">You should see a similar output.</figcaption></figure>
</div>



<p class="wp-block-paragraph">Now, if you&#8217;ve done everything correctly up to this point, you should be able to connect to the WordPress container via the created compute instance&#8217;s IP address. You can find the compute instance&#8217;s IP address in OCI. Keep in mind that the traffic between you (the client) and the server will be entirely unencrypted plaintext. If someone were to listen to the traffic, they would see everything (e.g., your WordPress admin credentials).</p>



<h3 class="wp-block-heading" id="configure-tls-ssl-encryption-i-e-https">Configure TLS/SSL Encryption (i.e., HTTPS)</h3>



<h4 class="wp-block-heading">Approaches</h4>



<p class="wp-block-paragraph">TLS/SSL not only makes WordPress more secure for you but also for anyone using your site. Fortunately, you have multiple options when setting it up. Here are all the approaches that I know of:</p>



<div class="wp-block-group is-layout-constrained wp-block-group-is-layout-constrained">
<figure class="wp-block-table"><table class="has-white-background-color has-background"><thead><tr><th>Approach</th><th>Difficulty</th><th>Security</th><th>Cost</th></tr></thead><tbody><tr><td><a href="https://www.entrust.com/resources/faq/what-is-a-self-signed-certificate"><em>self-signed certificates</em></a>.</td><td>Hardest</td><td>Security depends on how you sign them.</td><td>Free</td></tr><tr><td>Trusted <a href="https://stackoverflow.com/questions/40061263/what-is-ca-certificate-and-why-do-we-need-it"><em>CA-signed certificates</em></a> (e.g., <a href="https://letsencrypt.org/">Let&#8217;s Encrypt</a>).</td><td>Hard</td><td>Secure.</td><td>Requires a paid domain from a <a href="https://www.cloudflare.com/en-gb/learning/dns/glossary/what-is-a-domain-name-registrar/"><em>Domain Registrar</em></a>.</td></tr><tr><td>Cloudflare&#8217;s <a href="https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/flexible/"><em>Flexible</em></a> TLS/SSL protection mode.</td><td>Easy</td><td>For WordPress, this might suffice but<br><a href="https://r1ch.net/blog/dont-use-cloudflares-flexible-ssl"><em>comfort might come with a cost.</em></a></td><td>Requires a paid domain from a domain registrar.</td></tr></tbody></table><figcaption class="wp-element-caption">This table should give you an idea of all the different TLS/SSL approaches and the advantages/disadvantages of each.</figcaption></figure>
</div>



<p class="wp-block-paragraph">I&#8217;m personally a huge fan of&nbsp;<a href="https://www.cloudflare.com/en-gb/" target="_blank" rel="noreferrer noopener">Cloudflare</a>. They provide&nbsp;<a href="https://www.hipex.io/en/cloudflare/" target="_blank" rel="noreferrer noopener"><em>massive amounts of services</em></a>&nbsp;like hiding the ownership and tenure of a domain name, DDoS protection, and a free CA-signed certificate for a maximum of 15 years (after which it can be renewed for free). If you have a domain name (or plan to buy one), you&nbsp;<em>might</em>&nbsp;get off scot-free from all the complexity by using the Flexible protection mode mentioned in the table above. You only have to buy a domain from Cloudflare or&nbsp;<a href="https://developers.cloudflare.com/learning-paths/get-started/#add-a-domain-to-cloudflare" target="_blank" rel="noreferrer noopener"><em>register one with their DNS servers</em></a>&nbsp;and flip it on.&nbsp;<strong>But be aware that it won&#8217;t provide end-to-end (E2E) encryption.</strong></p>



<p class="wp-block-paragraph">If you have the know-how and don&#8217;t want to buy a domain, you can walk the self-signed certificate path. Remember that out of all the aforementioned approaches, it&#8217;s the hardest one (in my humble opinion). With that said, I can <a href="https://devopscube.com/create-self-signed-certificates-openssl/"><em>point you in the right direction</em></a>.</p>



<h4 class="wp-block-heading">The Winner</h4>



<p class="wp-block-paragraph">My recommendation is to use Trusted CA-signed certificates. They not only provide E2E encryption but, contrary to self-signed ones, <a href="https://franciscosalvetti.com/how-to-get-green-padlock"><em>mark your website as safe to use</em></a>. As I stand behind all my recommendations, this will be my path. For you to follow me, <strong>you must have a domain at your disposal.</strong> </p>



<h4 class="wp-block-heading" id="setting-up-ca-signed-certificates">Setting up CA-signed certificates</h4>



<h5 class="wp-block-heading">Reverse Proxy</h5>



<p class="wp-block-paragraph">When setting up TLS encryption it&#8217;s a good practice to isolate that responsibility to a wholly different server. I&#8217;ll be doing just that by setting up <a href="https://nginx.org/en/">NGINX</a> as a <a href="https://www.cloudflare.com/en-gb/learning/cdn/glossary/reverse-proxy/"><em>reverse proxy</em></a> for the upstream WordPress container. Due to the intrinsic nature of TLS, this will take some additional work, but no panic! It&#8217;ll be over in a heartbeat.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">Shut the containers and start a Certbot one.</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>sudo docker compose down
sudo docker run -p 80:80 -it &#8211;rm &#8211;name certbot -v &#8220;./data/certbot/conf:/etc/letsencrypt&#8221; -v &#8220;./data/certbot/www:/var/www/certbot&#8221; certbot/certbot certonly</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #FFCB6B">sudo</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">docker</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">compose</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">down</span></span>
<span class="line"><span style="color: #FFCB6B">sudo</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">docker</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">run</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-p</span><span style="color: #BABED8"> </span><span style="color: #F78C6C">80</span><span style="color: #C3E88D">:80</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-it</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">--rm</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">--name</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">certbot</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-v</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&quot;</span><span style="color: #C3E88D">./data/certbot/conf:/etc/letsencrypt</span><span style="color: #89DDFF">&quot;</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-v</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&quot;</span><span style="color: #C3E88D">./data/certbot/www:/var/www/certbot</span><span style="color: #89DDFF">&quot;</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">certbot/certbot</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">certonly</span></span></code></pre></div>



<p class="wp-block-paragraph">After starting up the ephemeral Certbot container (depicted by the -rm parameter), you must fill in a short form. It&#8217;s <em>very important</em> that you answer &#8220;1&#8221; to the first question. Whatever your domain is will define the answer to the second (and probably last) question.</p>



<p class="wp-block-paragraph">Afterwards, create a <em>data</em> folder and inside create a folder named <em>nginx</em>. Inside the <em>nginx</em> folder create the following file <strong>remembering to change the highlighted parts with your own domain</strong>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(159, 170, 243, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">Create ./data/nginx/nginx.conf </span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>upstream wordpress {
  keepalive 3;
  server wordpress:443;
  server wordpress:80;
}

server {
  listen 80;
  server_name example-domain.com;

  # Let&#8217;s Encrypt has to perform Domain Validation. 
  # It considers a domain validated, if it receives a 
  # certain response (i.e., the challenge) from a well 
  # known URL such as the below:
  location /.well-known/acme-challenge/ {
    root /var/www/certbot;
  }

  # Redirect HTTP requests to their HTTPS counterparts.
  location / {
    return 301 https://$host$request_uri;
  }
}

server {
 listen 443 ssl;
 server_name example-domain.com;
 ssl_certificate /etc/letsencrypt/live/example-domain.com/fullchain.pem;
 ssl_certificate_key /etc/letsencrypt/live/example-domain.com/privkey.pem;

 # Let&#8217;s Encrypt&#8217;s best-practice HTTPS configurations for NGINX:
 include /etc/letsencrypt/options-ssl-nginx.conf;
 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

 location / {
  proxy_pass http://wordpress;

  # If this isn&#8217;t included, the upstream WordPress 
  # server won&#8217;t be able to serve content correctly.
  proxy_set_header    Host                $host;

  # Not hard requirements per se, but a good practice.
  proxy_set_header    X-Real-IP           $remote_addr;
  proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
  proxy_set_header   &#8220;Connection&#8221; &#8220;&#8221;;
 }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #C792EA">upstream</span><span style="color: #BABED8"> </span><span style="color: #FFCB6B">wordpress </span><span style="color: #BABED8">{</span></span>
<span class="line"><span style="color: #BABED8"> </span><span style="color: #89DDFF"> keepalive </span><span style="color: #BABED8">3</span><span style="color: #89DDFF">;</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #89DDFF">server</span><span style="color: #BABED8"> wordpress:443;</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #89DDFF">server</span><span style="color: #BABED8"> wordpress:80;</span></span>
<span class="line"><span style="color: #BABED8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C792EA">server</span><span style="color: #BABED8"> {</span></span>
<span class="line"><span style="color: #BABED8"> </span><span style="color: #89DDFF"> listen </span><span style="color: #BABED8">80</span><span style="color: #89DDFF">;</span></span>
<span class="line cbp-line-highlight"><span style="color: #BABED8"> </span><span style="color: #89DDFF"> server_name </span><span style="color: #BABED8">example-domain.com</span><span style="color: #89DDFF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #464B5D; font-style: italic"># Let&#39;s Encrypt has to perform Domain Validation. </span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #464B5D; font-style: italic"># It considers a domain validated, if it receives a </span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #464B5D; font-style: italic"># certain response (i.e., the challenge) from a well </span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #464B5D; font-style: italic"># known URL such as the below:</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #C792EA">location</span><span style="color: #BABED8"> </span><span style="color: #FFCB6B">/.well-known/acme-challenge/ </span><span style="color: #BABED8">{</span></span>
<span class="line"><span style="color: #BABED8">   </span><span style="color: #89DDFF"> root </span><span style="color: #BABED8">/var/www/certbot</span><span style="color: #89DDFF">;</span></span>
<span class="line"><span style="color: #BABED8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #464B5D; font-style: italic"># Redirect HTTP requests to their HTTPS counterparts.</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #C792EA">location</span><span style="color: #BABED8"> </span><span style="color: #FFCB6B">/ </span><span style="color: #BABED8">{</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #89DDFF; font-style: italic">return</span><span style="color: #BABED8"> </span><span style="color: #F78C6C">301</span><span style="color: #BABED8"> https://</span><span style="color: #89DDFF">$</span><span style="color: #BABED8">host</span><span style="color: #89DDFF">$</span><span style="color: #BABED8">request_uri;</span></span>
<span class="line"><span style="color: #BABED8">  }</span></span>
<span class="line"><span style="color: #BABED8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C792EA">server</span><span style="color: #BABED8"> {</span></span>
<span class="line"><span style="color: #89DDFF"> listen </span><span style="color: #BABED8">443 ssl</span><span style="color: #89DDFF">;</span></span>
<span class="line cbp-line-highlight"><span style="color: #89DDFF"> server_name </span><span style="color: #BABED8">example-domain.com</span><span style="color: #89DDFF">;</span></span>
<span class="line cbp-line-highlight"><span style="color: #89DDFF"> ssl_certificate </span><span style="color: #BABED8">/etc/letsencrypt/live/example-domain.com/fullchain.pem</span><span style="color: #89DDFF">;</span></span>
<span class="line cbp-line-highlight"><span style="color: #89DDFF"> ssl_certificate_key </span><span style="color: #BABED8">/etc/letsencrypt/live/example-domain.com/privkey.pem</span><span style="color: #89DDFF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #BABED8"> </span><span style="color: #464B5D; font-style: italic"># Let&#39;s Encrypt&#39;s best-practice HTTPS configurations for NGINX:</span></span>
<span class="line"><span style="color: #89DDFF"> include </span><span style="color: #BABED8">/etc/letsencrypt/options-ssl-nginx.conf</span><span style="color: #89DDFF">;</span></span>
<span class="line"><span style="color: #89DDFF"> ssl_dhparam </span><span style="color: #BABED8">/etc/letsencrypt/ssl-dhparams.pem</span><span style="color: #89DDFF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #BABED8"> </span><span style="color: #C792EA">location</span><span style="color: #BABED8"> </span><span style="color: #FFCB6B">/ </span><span style="color: #BABED8">{</span></span>
<span class="line"><span style="color: #BABED8"> </span><span style="color: #89DDFF"> proxy_pass </span><span style="color: #BABED8">http://wordpress</span><span style="color: #89DDFF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #464B5D; font-style: italic"># If this isn&#39;t included, the upstream WordPress </span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #464B5D; font-style: italic"># server won&#39;t be able to serve content correctly.</span></span>
<span class="line"><span style="color: #BABED8"> </span><span style="color: #89DDFF"> proxy_set_header </span><span style="color: #BABED8">   Host                </span><span style="color: #89DDFF">$</span><span style="color: #BABED8">host</span><span style="color: #89DDFF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #464B5D; font-style: italic"># Not hard requirements per se, but a good practice.</span></span>
<span class="line"><span style="color: #BABED8"> </span><span style="color: #89DDFF"> proxy_set_header </span><span style="color: #BABED8">   X-Real-IP           </span><span style="color: #89DDFF">$</span><span style="color: #BABED8">remote_addr</span><span style="color: #89DDFF">;</span></span>
<span class="line"><span style="color: #BABED8"> </span><span style="color: #89DDFF"> proxy_set_header </span><span style="color: #BABED8">   X-Forwarded-For     </span><span style="color: #89DDFF">$</span><span style="color: #BABED8">proxy_add_x_forwarded_for</span><span style="color: #89DDFF">;</span></span>
<span class="line"><span style="color: #BABED8"> </span><span style="color: #89DDFF"> proxy_set_header </span><span style="color: #BABED8">  </span><span style="color: #C3E88D">&quot;Connection&quot;</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&quot;&quot;</span><span style="color: #89DDFF">;</span></span>
<span class="line"><span style="color: #BABED8"> }</span></span>
<span class="line"><span style="color: #BABED8">}</span></span></code></pre></div>



<p class="wp-block-paragraph">This file will be your NGINX container&#8217;s <em><a href="https://www.mulesoft.com/resources/esb/what-is-single-source-of-truth-ssot">single source of truth</a></em> on configuration. In all honesty, NGINX is its own world altogether so I&#8217;m not going to delve into every teeny tiny bit as I&#8217;m probably as clueless as you. On a high level, <strong>this configuration will make NGINX act as a reverse proxy for upstream servers all the while encrypting the HTTP traffic making it HTTPS.</strong></p>



<h5 class="wp-block-heading">Fetching Best-Practice HTTPS Configurations</h5>



<p class="wp-block-paragraph">As these are included in the configuration file, we need to fetch them and move them to the appropriate directory. <strong>These files aren&#8217;t mandatory and can be omitted from the configuration but they&#8217;re recommended for additional security.</strong> Also, your SSL report from <a href="https://www.ssllabs.com/ssltest/">Qualys SSL Labs</a> will be better.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">Fetch options-ssl-nginx.conf and ssl-dhparams.pem</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>sudo curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > ./data/certbot/conf/options-ssl-nginx.conf
sudo curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > ./data/certbot/conf/ssl-dhparams.pem</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #FFCB6B">sudo</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">curl</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-s</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&gt;</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">./data/certbot/conf/options-ssl-nginx.conf</span></span>
<span class="line"><span style="color: #FFCB6B">sudo</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">curl</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-s</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&gt;</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">./data/certbot/conf/ssl-dhparams.pem</span></span></code></pre></div>



<h5 class="wp-block-heading">Finalizing Docker Compose Manifest</h5>



<p class="wp-block-paragraph">Afterwards edit the Docker Compose manifest file accordingly:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-blur-enabled" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(159, 170, 243, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">Edit the ./docker-compose.yml file.</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>version: &#8216;3.8&#8217;

services:
  wordpress:
    image: arm64v8/wordpress
    restart: always
    ports:
      &#8211; &#8220;443&#8221;
      &#8211; &#8220;80&#8221;
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: &lt;DB_USER_HERE>
      WORDPRESS_DB_PASSWORD: &lt;DB_PASSWORD_HERE>
      WORDPRESS_DB_NAME: &lt;DB_NAME_HERE>
    volumes:
      &#8211; wordpress:/var/www/html
  db:
    image: arm64v8/mysql
    restart: always
    environment:
      MYSQL_DATABASE: &lt;DB_NAME_HERE>
      MYSQL_USER: &lt;DB_USER_HERE>
      MYSQL_PASSWORD: &lt;DB_PASSWORD_HERE>
      MYSQL_RANDOM_ROOT_PASSWORD: &#8216;1&#8217;
    volumes:
      &#8211; db:/var/lib/mysql
  nginx-rp:
    image: arm64v8/nginx
    container_name: nginx-rp
    depends_on:
      &#8211; wordpress
      &#8211; db
    restart: always
    ports:
      &#8211; &#8220;80:80&#8221;
      &#8211; &#8220;443:443&#8221;
    volumes:
      &#8211; ./data/certbot/conf:/etc/letsencrypt
      &#8211; ./data/certbot/www:/var/www/certbot
      &#8211; ./data/nginx:/etc/nginx/conf.d
volumes:
  wordpress:
  db:</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #F07178">version</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&#39;</span><span style="color: #C3E88D">3.8</span><span style="color: #89DDFF">&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F07178">services</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #F07178">wordpress</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">image</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">arm64v8/wordpress</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">restart</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">always</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">ports</span><span style="color: #89DDFF">:</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&quot;</span><span style="color: #C3E88D">443</span><span style="color: #89DDFF">&quot;</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&quot;</span><span style="color: #C3E88D">80</span><span style="color: #89DDFF">&quot;</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">environment</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">WORDPRESS_DB_HOST</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">db</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">WORDPRESS_DB_USER</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_USER_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">WORDPRESS_DB_PASSWORD</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_PASSWORD_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">WORDPRESS_DB_NAME</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_NAME_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">volumes</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">wordpress:/var/www/html</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #F07178">db</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">image</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">arm64v8/mysql</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">restart</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">always</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">environment</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">MYSQL_DATABASE</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_NAME_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">MYSQL_USER</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_USER_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">MYSQL_PASSWORD</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">&lt;DB_PASSWORD_HERE&gt;</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #F07178">MYSQL_RANDOM_ROOT_PASSWORD</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&#39;</span><span style="color: #C3E88D">1</span><span style="color: #89DDFF">&#39;</span></span>
<span class="line"><span style="color: #BABED8">    </span><span style="color: #F07178">volumes</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">db:/var/lib/mysql</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">  </span><span style="color: #F07178">nginx-rp</span><span style="color: #89DDFF">:</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">    </span><span style="color: #F07178">image</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">arm64v8/nginx</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">    </span><span style="color: #F07178">container_name</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">nginx-rp</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">    </span><span style="color: #F07178">depends_on</span><span style="color: #89DDFF">:</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">wordpress</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">db</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">    </span><span style="color: #F07178">restart</span><span style="color: #89DDFF">:</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">always</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">    </span><span style="color: #F07178">ports</span><span style="color: #89DDFF">:</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&quot;</span><span style="color: #C3E88D">80:80</span><span style="color: #89DDFF">&quot;</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&quot;</span><span style="color: #C3E88D">443:443</span><span style="color: #89DDFF">&quot;</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">    </span><span style="color: #F07178">volumes</span><span style="color: #89DDFF">:</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">./data/certbot/conf:/etc/letsencrypt</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">./data/certbot/www:/var/www/certbot</span></span>
<span class="line cbp-no-blur"><span style="color: #BABED8">      </span><span style="color: #89DDFF">-</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">./data/nginx:/etc/nginx/conf.d</span></span>
<span class="line"><span style="color: #F07178">volumes</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #F07178">wordpress</span><span style="color: #89DDFF">:</span></span>
<span class="line"><span style="color: #BABED8">  </span><span style="color: #F07178">db</span><span style="color: #89DDFF">:</span></span></code></pre></div>



<p class="wp-block-paragraph">As you can see, the host computer no longer routes HTTP (port 80) and HTTPS (port 443) traffic into the WordPress container but to the <em>nginx-rp</em> container. The WordPress container still accepts HTTP and HTTPS traffic, which is why <em>nginx-rp</em> can route traffic to the defined upstream server (rows 1-5 and 35-46 in the <em>nginx.conf</em>). </p>



<p class="wp-block-paragraph">Now you can start up the containers and hope for the best:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">Compose up the containers.</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>sudo docker compose up</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #FFCB6B">sudo</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">docker</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">compose</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">up</span></span></code></pre></div>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-f56f613f wp-block-group-is-layout-flex">
<p class="wp-block-paragraph">If all goes well, <strong>you can now access your WordPress platform via a browser by using the domain name</strong>. For me (as you probably can deduce), it&#8217;s&nbsp;<em>karnavaara.com</em>. Also, you should now notice that the connection (to the left of the address bar) is secure and encrypted.&nbsp;However, we still need to configure automatic renewal as the certificates expire after three months.  </p>



<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d29fcae&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d29fcae" class="wp-block-image size-full has-custom-border is-style-default wp-lightbox-container"><img loading="lazy" decoding="async" width="350" height="200" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-15.png" alt="" class="wp-image-1613" style="border-width:1px" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-15.png 350w, https://karnavaara.com/wp-content/uploads/2023/10/image-15-300x171.png 300w" sizes="auto, (max-width: 350px) 100vw, 350px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">How HTTPS looks like</figcaption></figure>
</div>



<h5 class="wp-block-heading" id="setting-up-automatic-renewal-of-certificates">Setting up automatic renewal</h5>



<p class="wp-block-paragraph">With the power of Linux this is more than doable<sup>(<em>maybe even millionable</em>)</sup>. We can utilize <a href="https://man7.org/linux/man-pages/man5/crontab.5.html">Crontab</a> (the instructor for the <a href="https://man7.org/linux/man-pages/man8/cron.8.html">Cron daemon</a>) and <a href="https://ryanstutorials.net/bash-scripting-tutorial/bash-script.php"><em>Bash scripts</em></a>. Both are, of course, preinstalled on Linux distributions. </p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">Create a bash script called renew_certs.sh:</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>nano ./renew_certs.sh</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #FFCB6B">nano</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">./renew_certs.sh</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">Add the following content to it:</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>#!/bin/bash

# Maybe redundant, but I have PTSD.
cd ~

# Run Certbot&#8217;s renew command in an ephemeral container called certbot:
sudo docker run -it &#8211;rm &#8211;name certbot -v &#8220;/etc/letsencrypt:/etc/letsencrypt&#8221; -v &#8220;/var/lib/letsencrypt:/var/lib/letsencrypt&#8221; certbot/certbot:arm64v8-latest renew

# Reload the NGINX running inside the nginx-rp container with the potential new certs:
sudo docker exec nginx-rp nginx -s reload</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #464B5D; font-style: italic">#!/bin/bash</span></span>
<span class="line"></span>
<span class="line"><span style="color: #464B5D; font-style: italic"># Maybe redundant, but I have PTSD.</span></span>
<span class="line"><span style="color: #82AAFF">cd</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">~</span></span>
<span class="line"></span>
<span class="line"><span style="color: #464B5D; font-style: italic"># Run Certbot&#39;s renew command in an ephemeral container called certbot:</span></span>
<span class="line"><span style="color: #FFCB6B">sudo</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">docker</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">run</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-it</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">--rm</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">--name</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">certbot</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-v</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&quot;</span><span style="color: #C3E88D">/etc/letsencrypt:/etc/letsencrypt</span><span style="color: #89DDFF">&quot;</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-v</span><span style="color: #BABED8"> </span><span style="color: #89DDFF">&quot;</span><span style="color: #C3E88D">/var/lib/letsencrypt:/var/lib/letsencrypt</span><span style="color: #89DDFF">&quot;</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">certbot/certbot:arm64v8-latest</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">renew</span></span>
<span class="line"></span>
<span class="line"><span style="color: #464B5D; font-style: italic"># Reload the NGINX running inside the nginx-rp container with the potential new certs:</span></span>
<span class="line"><span style="color: #FFCB6B">sudo</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">docker</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">exec</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">nginx-rp</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">nginx</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-s</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">reload</span></span></code></pre></div>



<p class="wp-block-paragraph">This script basically renews the certifications if they happen to expire. Next, we&#8217;ll use Crontab to run this script automatically every 12 hours. Why 12 hours? Because this was <a href="https://serverfault.com/a/825032">Let&#8217;s Encrypt&#8217;s recommendation in the past</a>. </p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">Open the root&#8217;s crontab file:</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>sudo crontab -e</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #FFCB6B">sudo</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">crontab</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">-e</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#babed8;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#181c2a;color:#aaafcf">Add this line to the bottom:</span><span role="button" tabindex="0" style="color:#babed8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>0 */12 * * * /home/ubuntu/renew_certs.sh</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki material-theme-ocean" style="background-color: #0F111A" tabindex="0"><code><span class="line"><span style="color: #FFCB6B">0</span><span style="color: #BABED8"> </span><span style="color: #BABED8">*</span><span style="color: #C3E88D">/12</span><span style="color: #BABED8"> </span><span style="color: #BABED8">*</span><span style="color: #BABED8"> </span><span style="color: #BABED8">*</span><span style="color: #BABED8"> </span><span style="color: #BABED8">*</span><span style="color: #BABED8"> </span><span style="color: #C3E88D">/home/ubuntu/renew_certs.sh</span></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-start;background-color:#0F111A;color:#aaafcf;font-size:12px;line-height:1;position:relative">If you&#8217;re using a different user, you have to change &#8220;ubuntu&#8221; to its name.</span></div>



<p class="wp-block-paragraph">I&#8217;m being honest here:&nbsp;<strong>This automatic renewal implementation is only theoretical, and I haven&#8217;t proven its feasibility in practice as my certificate hasn&#8217;t expired yet.</strong>&nbsp;It remains to be seen if this is practical. Though, I fail to see a reason why it would be impractical. </p>



<p class="wp-block-paragraph">We&#8217;re all done here. You hopefully now have a working implementation of WordPress running on a domain of your choice with a front NGINX reverse proxy that encrypts HTTP traffic into HTTPS.</p>



<h3 class="wp-block-heading" id="finish-the-installation-by-going-through-the-famous-five-minute-wordpress-installation-process">Go through the famous five-minute WordPress installation process.</h3>



<p class="wp-block-paragraph">This part is simple. The first time you open WordPress in a browser window via its URL, you must fill in <a href="https://developer.wordpress.org/advanced-administration/before-install/howto-install/#finishing-installation">a basic form</a>. Invent an obscure username and a strong password that&#8217;s impossible to practically brute force.</p>



<p class="wp-block-paragraph"><strong>If errors were to befall you, I&#8217;d wager a guess that you misconfigured the MySQL container in the Docker Compose manifest file.</strong> Ensure that the WordPress container&#8217;s database-related environment variables are correct. WordPress requires valid credentials to access the MySQL database.</p>



<h3 class="wp-block-heading" id="install-updraftplus-w3-total-cache-and-limit-login-attempts-reloaded-plugins-for-security-hardening-seo-faster-performance-and-resilience">Install plugins for security hardening, faster performance, and resilience.</h3>



<p class="wp-block-paragraph">There are thousands of plugins to choose from. This part is completely optional, although recommended as plugins provide your self-hosted WordPress instance with valuable functionality.</p>



<div class="wp-block-group is-layout-constrained wp-block-group-is-layout-constrained">
<figure data-wp-context="{&quot;imageId&quot;:&quot;69d1f4d2a05bc&quot;}" data-wp-interactive="core/image" data-wp-key="69d1f4d2a05bc" class="wp-block-image size-full has-custom-border wp-lightbox-container"><img loading="lazy" decoding="async" width="756" height="422" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://karnavaara.com/wp-content/uploads/2023/10/image-16.png" alt="As an admin user, you can add plugins from &quot;Plugins&quot; -&gt; &quot;Add New&quot;." class="wp-image-1640" style="border-width:1px" srcset="https://karnavaara.com/wp-content/uploads/2023/10/image-16.png 756w, https://karnavaara.com/wp-content/uploads/2023/10/image-16-300x167.png 300w" sizes="auto, (max-width: 756px) 100vw, 756px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button><figcaption class="wp-element-caption">As an admin user, you can add plugins from &#8220;Plugins&#8221; -&gt; &#8220;Add New&#8221;. </figcaption></figure>
</div>



<p class="wp-block-paragraph">I wholeheartedly recommend you install at least these plugins: </p>



<ul class="wp-block-list">
<li><em>Limit Login Attempts Reloaded</em> (LLAR) for security. 
<ul class="wp-block-list">
<li>It makes brute forcing much more difficult as this plugin can, for example, timeout IPs that exceed four login tries in under 20 minutes.</li>
</ul>
</li>



<li><em>W3 Total Cache</em> for performance.
<ul class="wp-block-list">
<li>This plugin can do a lot of things but as the name implies it caches your WordPress content. It can reduce server load through caching and content <a href="https://www.imperva.com/learn/performance/minification/" target="_blank" rel="noreferrer noopener"><em>minification</em></a>.</li>
</ul>
</li>



<li><em>UpdraftPlus</em> for <a href="https://www.f5.com/glossary/recovery-point-objective-rpo" target="_blank" rel="noreferrer noopener"><em>Recovery Point Objective</em></a> (RPO).
<ul class="wp-block-list">
<li>You can setup backups in OCI if you want, but it will increase your cloud bill (<em>and is probably a bit overkill</em>). You can use UpdraftPlus to implement your own RPO and without any cost (e.g., to Google Drive).</li>



<li>A very important note to take into account is <em>privacy</em>. I don&#8217;t know how much UpdraftPlus respects it. <strong>If you process confidential and personal information, I&#8217;d probably trust more in Oracle than UpdraftPlus.</strong></li>
</ul>
</li>



<li id="update1"><em>WPS Hide Login</em> for changing the default WordPress login URL.
<ul class="wp-block-list">
<li>Even though LLAR protects against brute forcing, it&#8217;s not enough. It would be prudent to add another security layer. With <em>WPS Hide Login</em> we can implement <a href="https://en.wikipedia.org/wiki/Security_through_obscurity" target="_blank" rel="noreferrer noopener">Security Through Obscurity</a> by obscuring the login URL.</li>



<li>Security Through Obscurity is a bad practice. Still, it&#8217;s better than nothing because it can slow and demotivate malicious actors as they have to resort to a technique called <a href="https://www.blackduck.com/glossary/what-is-fuzz-testing.html" target="_blank" rel="noreferrer noopener">fuzzing</a> to find the login URL, which you have set as a custom and irregular one.</li>



<li>&#8220;<em>While not a standalone solution, security through obscurity can complement other security measures in certain scenarios.</em>&#8221; –Wikipedia</li>
</ul>
</li>
</ul>



<h2 class="wp-block-heading">Finalmente</h2>



<p class="wp-block-paragraph">And that&#8217;s it! My title stands as this should cost you only pennies. If you joined me for the whole journey, you will probably pay only for storage (<a href="#mandatory-costs">mentioned here</a>) and the domain. Of course, you could do this on your home server (also known as <a href="https://www.insight.com/en_US/content-and-resources/glossary/o/on-premises.html"><em>on-premises</em></a>). But that could be far more complex and you&#8217;d be paying pennies for that<sup>(electricity)</sup> too if not more. </p>



<p class="wp-block-paragraph">I&#8217;ll&nbsp;<a href="#ssh-key-pro-tip">mention this</a>&nbsp;the final time: make it a habit to use a&nbsp;<em>secure</em>&nbsp;password manager and put your SSH keys there. <strong>Don&#8217;t save them locally in plaintext!</strong> Trust me, they quickly pile up while being hard to manage. On top of a lack of management, they increase your attack surface. What happens if your device gets stolen? Or what about if the device gets broken? Or what about if you buy a new computer and forget about the SSH keys altogether?</p>



<h2 class="wp-block-heading" id="supplementary">Supplementary</h2>


<ol class="wp-block-footnotes"><li id="019886f8-0e44-4fff-9bad-0ae7b9b42206">Consider this <a href="https://themes.gohugo.io/themes/github-style/"><em>GitHub-esque theme</em></a> (or maybe this <em><a href="https://github.com/frjo/hugo-theme-zen">zen theme</a></em>), if you decide to try Hugo. <a href="#019886f8-0e44-4fff-9bad-0ae7b9b42206-link" aria-label="Jump to footnote reference 1"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="448b6a3e-a196-46cd-a51a-8a9fb6717667">Maybe an insightful endeavor worth doing for a new <em>sapling</em>. <a href="#448b6a3e-a196-46cd-a51a-8a9fb6717667-link" aria-label="Jump to footnote reference 2"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="e4e1333e-3fa7-458e-95b1-93d1fec2a166">Also known as Karnavaara – the Forested Hill of Insight. <a href="#e4e1333e-3fa7-458e-95b1-93d1fec2a166-link" aria-label="Jump to footnote reference 3"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="97810694-81d6-4b55-b75c-88bf796dc46b">Though, paradoxically, there are still <a href="https://www.freecodecamp.org/news/7-cases-when-not-to-use-docker/"><em>valid reasons</em></a> to steer away from using Docker. You can, for example, opt-in to use <a href="https://www.vagrantup.com/">Vagrant</a> instead. <a href="#97810694-81d6-4b55-b75c-88bf796dc46b-link" aria-label="Jump to footnote reference 4"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="fe6fded5-9050-4c17-8de9-95466bd380f6">Docker images are Open Container Initiative (OCI) compliant. <a href="#fe6fded5-9050-4c17-8de9-95466bd380f6-link" aria-label="Jump to footnote reference 5"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li></ol>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Lazy Loading (feed)

Served from: karnavaara.com @ 2026-04-05 08:36:18 by W3 Total Cache
-->