<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Ninthcircle]]></title><description><![CDATA[Ninthcircle]]></description><link>https://blogs.9th.fun</link><generator>RSS for Node</generator><lastBuildDate>Thu, 09 Apr 2026 20:54:26 GMT</lastBuildDate><atom:link href="https://blogs.9th.fun/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Setting Up a Private Gitea Instance on GCP]]></title><description><![CDATA[Managing your own Git server gives full control over repositories while keeping access private. In this post, I document the process of setting up Gitea on a Google Cloud Platform (GCP) e2-micro VM, including the problems encountered and how they wer...]]></description><link>https://blogs.9th.fun/private-gitea-instance-on-gcp</link><guid isPermaLink="true">https://blogs.9th.fun/private-gitea-instance-on-gcp</guid><category><![CDATA[gitea]]></category><category><![CDATA[GCP]]></category><category><![CDATA[self-hosted]]></category><dc:creator><![CDATA[Nosferatu]]></dc:creator><pubDate>Tue, 09 Sep 2025 20:52:15 GMT</pubDate><content:encoded><![CDATA[<p>Managing your own Git server gives full control over repositories while keeping access private. In this post, I document the process of setting up <strong>Gitea</strong> on a <strong>Google Cloud Platform (GCP) e2-micro VM</strong>, including the problems encountered and how they were resolved.</p>
<blockquote>
<p><em>Written with assistance from ChatGPT.</em></p>
</blockquote>
<p>Checkout my instance: &lt;https://gitea.9th.fun/&gt;</p>
<h2 id="heading-1-setting-up-the-vm"><strong>1. Setting Up the VM</strong></h2>
<p>I started with GCP’s <strong>Free Tier e2-micro VM</strong>:</p>
<ul>
<li><p>1 non-preemptible e2-micro VM in a US region</p>
</li>
<li><p>30 GB persistent disk</p>
</li>
<li><p>1 GB outbound data transfer</p>
</li>
</ul>
<p><strong>Firewall setup</strong>:</p>
<ul>
<li><p>Allowed HTTP (80) and HTTPS (443) traffic</p>
</li>
<li><p>SSH left open for private access</p>
</li>
</ul>
<p><strong>Static IP</strong>:</p>
<ul>
<li>Reserved a static IP for consistent domain mapping</li>
</ul>
<h2 id="heading-2-preparing-the-vm"><strong>2. Preparing the VM</strong></h2>
<p>Before connecting to the VM, generate a <strong>SSH key pair</strong> for secure authentication.</p>
<h3 id="heading-21-generate-an-ssh-key-pair"><strong>2.1: Generate an SSH key pair</strong></h3>
<p>On <strong>Windows</strong> (PowerShell or Git Bash):</p>
<pre><code class="lang-powershell"><span class="hljs-comment"># Create a folder for keys</span>
mkdir C:\Users\&lt;YOUR_USER&gt;\Keys\gitea

<span class="hljs-comment"># Generate an ed25519 SSH key pair</span>
ssh<span class="hljs-literal">-keygen</span> <span class="hljs-literal">-t</span> ed25519 <span class="hljs-literal">-C</span> <span class="hljs-string">"USERNAME@gitea-vm"</span> <span class="hljs-operator">-f</span> C:\Users\&lt;YOUR_USER&gt;\Keys\gitea\id_ed25519
</code></pre>
<ul>
<li><p>Optional: Enter a passphrase for extra security.</p>
</li>
<li><p>This generates:</p>
</li>
</ul>
<pre><code class="lang-powershell">C:\Users\&lt;YOUR_USER&gt;\Keys\gitea\id_ed25519       <span class="hljs-comment"># Private key (keep secure!)</span>
C:\Users\&lt;YOUR_USER&gt;\Keys\gitea\id_ed25519.pub   <span class="hljs-comment"># Public key (upload to VM)</span>
</code></pre>
<h3 id="heading-22-add-the-public-key-to-the-vm"><strong>2.2: Add the public key to the VM</strong></h3>
<ul>
<li><p>During VM creation, paste the public key in the <strong>SSH keys section</strong> of the GCP Console.</p>
</li>
<li><p>Alternatively, after VM creation:</p>
</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-comment"># On VM, create SSH directory for the user</span>
mkdir -p ~/.ssh
<span class="hljs-built_in">echo</span> <span class="hljs-string">"&lt;paste-your-public-key-here&gt;"</span> &gt;&gt; ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
</code></pre>
<p><strong>Files involved</strong>:</p>
<ul>
<li><p><code>~/.ssh/authorized_keys</code> → Stores the public keys allowed to connect</p>
</li>
<li><p><code>~/.ssh/</code> → SSH directory</p>
</li>
</ul>
<h3 id="heading-23-test-ssh-connection"><strong>2.3: Test SSH connection</strong></h3>
<p>On Windows:</p>
<pre><code class="lang-powershell">ssh <span class="hljs-literal">-i</span> C:\Users\&lt;YOUR_USER&gt;\Keys\gitea\id_ed25519 USERNAME<span class="hljs-selector-tag">@</span>&lt;VM_IP&gt;
</code></pre>
<h3 id="heading-24-update-ssh-config-for-convenience"><strong>2.4: Update SSH config for convenience</strong></h3>
<p>Edit <code>C:\Users\&lt;YOUR_USER&gt;\.ssh\config</code>:</p>
<pre><code class="lang-plaintext">Host gitea.example.com
  HostName &lt;VM_IP&gt;
  User USERNAME
  IdentityFile C:\Users\&lt;YOUR_USER&gt;\Keys\gitea\id_ed25519
  Port 22
</code></pre>
<h2 id="heading-3-installing-gitea"><strong>3. Installing Gitea</strong></h2>
<h3 id="heading-31-create-necessary-directories">3.1: Create necessary directories</h3>
<pre><code class="lang-bash">sudo mkdir -p /var/lib/gitea/{custom,data,<span class="hljs-built_in">log</span>}
sudo chown -R git:git /var/lib/gitea
</code></pre>
<p><strong>Directories involved</strong>:</p>
<ul>
<li><p><code>/var/lib/gitea/custom/</code> → Custom configs/templates</p>
</li>
<li><p><code>/var/lib/gitea/data/</code> → App data, database</p>
</li>
<li><p><code>/var/lib/gitea/log/</code> → Logs</p>
</li>
</ul>
<h3 id="heading-32-download-gitea-binary">3.2: Download Gitea binary</h3>
<pre><code class="lang-bash">wget -O /usr/<span class="hljs-built_in">local</span>/bin/gitea https://dl.gitea.io/gitea/1.21.0/gitea-1.21.0-linux-amd64
sudo chmod +x /usr/<span class="hljs-built_in">local</span>/bin/gitea
</code></pre>
<p><strong>File involved</strong>:</p>
<ul>
<li><code>/usr/local/bin/gitea</code> → Main Gitea executable</li>
</ul>
<h3 id="heading-33-create-a-systemd-service-at-etcsystemdsystemgiteaservice">3.3: Create a <strong>systemd service</strong> at <code>/etc/systemd/system/gitea.service</code></h3>
<pre><code class="lang-ini"><span class="hljs-section">[Unit]</span>
<span class="hljs-attr">Description</span>=Gitea (Git with a cup of tea)
<span class="hljs-attr">After</span>=network.target

<span class="hljs-section">[Service]</span>
<span class="hljs-attr">RestartSec</span>=<span class="hljs-number">2</span>s
<span class="hljs-attr">Type</span>=simple
<span class="hljs-attr">User</span>=git
<span class="hljs-attr">Group</span>=git
<span class="hljs-attr">WorkingDirectory</span>=/var/lib/gitea
<span class="hljs-attr">Environment</span>=USER=git HOME=/var/lib/gitea GITEA_WORK_DIR=/var/lib/gitea
<span class="hljs-attr">ExecStart</span>=/usr/local/bin/gitea web -c /etc/gitea/app.ini

<span class="hljs-section">[Install]</span>
<span class="hljs-attr">WantedBy</span>=multi-user.target
</code></pre>
<h2 id="heading-4-nginx-reverse-proxy-setup"><strong>4. Nginx Reverse Proxy Setup</strong></h2>
<h3 id="heading-41-create-a-file-etcnginxsites-availablegitea">4.1: Create a file <code>/etc/nginx/sites-available/gitea</code></h3>
<pre><code class="lang-nginx"><span class="hljs-section">server</span> {
    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;
    <span class="hljs-attribute">server_name</span> gitea.example.com;

    <span class="hljs-attribute">location</span> /.well-known/acme-challenge/ {
        <span class="hljs-attribute">root</span> /var/www/html;
    }

    <span class="hljs-attribute">location</span> / {
        <span class="hljs-attribute">proxy_pass</span> http://127.0.0.1:3000;
        <span class="hljs-attribute">proxy_set_header</span> X-Real-IP <span class="hljs-variable">$remote_addr</span>;
        <span class="hljs-attribute">proxy_set_header</span> X-Forwarded-For <span class="hljs-variable">$proxy_add_x_forwarded_for</span>;
        <span class="hljs-attribute">proxy_set_header</span> Host <span class="hljs-variable">$http_host</span>;
        <span class="hljs-attribute">proxy_set_header</span> X-Forwarded-Proto <span class="hljs-variable">$scheme</span>;
        <span class="hljs-attribute">proxy_read_timeout</span> <span class="hljs-number">90</span>;
    }
}
</code></pre>
<h3 id="heading-42-enable-the-site-and-reload-nginx">4.2: Enable the site and reload Nginx</h3>
<pre><code class="lang-bash">sudo ln -s /etc/nginx/sites-available/gitea /etc/nginx/sites-enabled/gitea
sudo nginx -t
sudo systemctl reload nginx
</code></pre>
<p><strong>Files involved</strong>:</p>
<ul>
<li><p><code>/etc/nginx/sites-available/gitea</code> → Nginx config for Gitea</p>
</li>
<li><p><code>/etc/nginx/sites-enabled/gitea</code> → Symlink to enable site</p>
</li>
</ul>
<h2 id="heading-5-initial-gitea-setup"><strong>5. Initial Gitea Setup</strong></h2>
<ul>
<li><p>Visit <a target="_blank" href="http://gitea.example.com"><code>http://gitea.example.com</code></a> to complete the installation.</p>
</li>
<li><p>Use <strong>SQLite</strong> for simplicity.</p>
</li>
<li><p>Create an <strong>admin user</strong>.</p>
</li>
</ul>
<p><strong>Files involved</strong>:</p>
<ul>
<li><p><code>/etc/gitea/app.ini</code> → Main Gitea configuration</p>
</li>
<li><p><code>/var/lib/gitea/data/gitea.db</code> → SQLite database</p>
</li>
</ul>
<h2 id="heading-6-enabling-ssh-for-gitea"><strong>6. Enabling SSH for Gitea</strong></h2>
<p>Update <code>/etc/gitea/app.ini</code>:</p>
<pre><code class="lang-ini"><span class="hljs-section">[server]</span>
<span class="hljs-attr">DISABLE_SSH</span> = <span class="hljs-literal">false</span>
<span class="hljs-attr">SSH_PORT</span> = <span class="hljs-number">2222</span>
<span class="hljs-attr">SSH_DOMAIN</span> = gitea.example.com
<span class="hljs-attr">START_SSH_SERVER</span> = <span class="hljs-literal">true</span>
</code></pre>
<p><strong>Problem faced</strong>:</p>
<ul>
<li><p>Ports <code>&lt;1024</code> require root; Gitea runs as non-root.</p>
</li>
<li><p>Service failed with: <code>Failed to start SSH server: listen tcp :22: bind: permission denied</code>.</p>
</li>
</ul>
<p><strong>Solution</strong>:</p>
<ul>
<li><p>Use a <strong>non-privileged SSH port</strong> (e.g., 2222).</p>
</li>
<li><p>Restart Gitea to activate SSH:</p>
</li>
</ul>
<pre><code class="lang-bash">sudo systemctl restart gitea
</code></pre>
<h2 id="heading-7-configuring-windows-ssh"><strong>7. Configuring Windows SSH</strong></h2>
<p>Update <code>C:\Users\&lt;YOUR_USER&gt;\.ssh\config</code>:</p>
<pre><code class="lang-plaintext">Host gitea.example.com
  User git
  IdentityFile C:\Users\&lt;YOUR_USER&gt;\Keys\gitea\id_ed25519
  Port 2222
</code></pre>
<p>Test connection:</p>
<pre><code class="lang-powershell">ssh <span class="hljs-literal">-v</span> git@gitea.example.com
</code></pre>
<h2 id="heading-8-firewall-amp-networking"><strong>8. Firewall &amp; Networking</strong></h2>
<ul>
<li><p>Open <strong>TCP port 2222</strong> in GCP firewall rules.</p>
</li>
<li><p>Cloudflare only proxies HTTP/HTTPS → cannot proxy SSH.</p>
</li>
<li><p>Use <strong>VM public IP</strong> for SSH pushes.</p>
</li>
</ul>
<p>Example Git remote:</p>
<pre><code class="lang-powershell">git remote <span class="hljs-built_in">set-url</span> gitea ssh://git@gitea.example.com:<span class="hljs-number">2222</span>/USERNAME/&lt;repo_name&gt;.git
git push <span class="hljs-literal">-u</span> gitea master
</code></pre>
<h2 id="heading-9-final-notes"><strong>9. Final Notes</strong></h2>
<ul>
<li><p><strong>Private Gitea</strong>: Only admin can create accounts (<code>DISABLE_REGISTRATION = true</code>).</p>
</li>
<li><p><strong>HTTP/HTTPS</strong>: Served via Nginx, optionally protected by Cloudflare.</p>
</li>
<li><p><strong>SSH for Git</strong>: Non-privileged port (2222), keys registered in Gitea.</p>
</li>
<li><p><strong>File paths modified/created</strong>:</p>
</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td>File/Directory</td><td>Purpose</td></tr>
</thead>
<tbody>
<tr>
<td><code>C:\Users\&lt;YOUR_USER&gt;\Keys\gitea/id_ed25519*</code></td><td>SSH keys</td></tr>
<tr>
<td><code>~/.ssh/authorized_keys</code></td><td>Public key storage</td></tr>
<tr>
<td><code>C:\Users\&lt;YOUR_USER&gt;\.ssh/config</code></td><td>SSH config for convenience</td></tr>
<tr>
<td><code>/usr/local/bin/gitea</code></td><td>Gitea executable</td></tr>
<tr>
<td><code>/etc/systemd/system/gitea.service</code></td><td>systemd service definition</td></tr>
<tr>
<td><code>/var/lib/gitea/{custom,data,log}</code></td><td>Gitea directories</td></tr>
<tr>
<td><code>/etc/gitea/app.ini</code></td><td>Gitea config</td></tr>
<tr>
<td><code>/etc/nginx/sites-available/gitea</code></td><td>Nginx config</td></tr>
<tr>
<td><code>/etc/nginx/sites-enabled/gitea</code></td><td>Nginx symlink</td></tr>
</tbody>
</table>
</div><hr />
<blockquote>
<p>✅ <strong>Outcome</strong>: Fully functional private Gitea instance on GCP:</p>
<ul>
<li><p>Web UI via <a target="_blank" href="https://gitea.example.com"><code>https://gitea.example.com</code></a></p>
</li>
<li><p>Git push/pull via SSH on port 2222</p>
</li>
<li><p>Admin-only registration</p>
</li>
<li><p>Secure key-based authentication</p>
</li>
</ul>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Clojure environment on Linux with Emacs]]></title><description><![CDATA[This blog provides a comprehensive guide to setting Up a Clojure development environment on Linux with Emacs.
Install JDK (Preferably OpenJDK)
To get started, install OpenJDK. For example, download version 23.0.2 from the official site:
wget https://...]]></description><link>https://blogs.9th.fun/clojure-environment-on-linux-with-emacs</link><guid isPermaLink="true">https://blogs.9th.fun/clojure-environment-on-linux-with-emacs</guid><category><![CDATA[Clojure]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Emacs]]></category><category><![CDATA[lsp]]></category><dc:creator><![CDATA[Nosferatu]]></dc:creator><pubDate>Sun, 16 Mar 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p>This blog provides a comprehensive guide to setting Up a Clojure development environment on Linux with Emacs.</p>
<h2 id="heading-install-jdk-preferably-openjdk"><strong>Install JDK (Preferably OpenJDK)</strong></h2>
<p>To get started, install OpenJDK. For example, download version 23.0.2 from the <a target="_blank" href="https://jdk.java.net/23/"><strong>official site</strong></a>:</p>
<pre><code class="lang-bash">wget https://download.java.net/java/GA/jdk23.0.2/6da2a6609d6e406f85c491fcb119101b/7/GPL/openjdk-23.0.2_linux-x64_bin.tar.gz
tar -xvf openjdk-23.0.2_linux-x64_bin.tar.gz
</code></pre>
<h2 id="heading-add-jdk-to-path"><strong>Add JDK to PATH</strong></h2>
<p>To ensure your system recognizes the JDK, set up the <code>JAVA_HOME</code> environment variable and update your <code>PATH</code>.</p>
<p>For Bash, add the following lines to <code>~/.bashrc</code>:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> JAVA_HOME=/path/to/your/jdk-23.0.2
<span class="hljs-built_in">export</span> PATH=<span class="hljs-variable">$JAVA_HOME</span>/bin:<span class="hljs-variable">$PATH</span>
</code></pre>
<p>For Fish, update your <code>~/.config/fish/config.fish</code>:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">set</span> -x JAVA_HOME /path/to/your/jdk-23.0.2
<span class="hljs-built_in">set</span> -U fish_user_paths /path/to/your/jdk-23.0.2/bin <span class="hljs-variable">$fish_user_paths</span>
</code></pre>
<h2 id="heading-install-clojure"><strong>Install Clojure</strong></h2>
<p>To install Clojure, run the following commands:</p>
<pre><code class="lang-bash">curl -L -O https://github.com/clojure/brew-install/releases/latest/download/linux-install.sh
chmod +x linux-install.sh
sudo ./linux-install.sh
</code></pre>
<blockquote>
<p>For more details, visit the <a target="_blank" href="https://clojure.org/guides/install_clojure"><strong>official guide</strong></a>.</p>
</blockquote>
<h2 id="heading-install-clojure-mode-for-emacs"><strong>Install clojure-mode for Emacs</strong></h2>
<p>To enable Clojure support in Emacs, install clojure-mode:</p>
<pre><code class="lang-plaintext">M-x package-install [RET] clojure-mode [RET]
</code></pre>
<blockquote>
<p>For more information, check the <a target="_blank" href="https://github.com/clojure-emacs/clojure-mode"><strong>clojure-mode repository</strong></a>.</p>
</blockquote>
<h2 id="heading-install-clojure-lsp"><strong>Install clojure-lsp</strong></h2>
<p>clojure-lsp provides language server support for Clojure. Install it using:</p>
<pre><code class="lang-bash">curl -s https://raw.githubusercontent.com/clojure-lsp/clojure-lsp/master/install | sudo bash
</code></pre>
<blockquote>
<p>More details are available on the <a target="_blank" href="https://clojure-lsp.io/installation/"><strong>clojure-lsp installation page</strong></a>.</p>
</blockquote>
<h2 id="heading-configure-clojure-lsp-for-emacs"><strong>Configure clojure-lsp for Emacs</strong></h2>
<h3 id="heading-using-eglot"><strong>Using</strong> <code>eglot</code>:</h3>
<p>If you prefer <code>eglot</code>, configure it as follows:</p>
<pre><code class="lang-lisp"><span class="hljs-comment">;; clojure-lsp configuration</span>
(<span class="hljs-name">use-package</span> eglot
  <span class="hljs-symbol">:ensure</span> <span class="hljs-literal">t</span>
  <span class="hljs-symbol">:hook</span> ((<span class="hljs-name">clojure-mode</span> . eglot-ensure)
         (<span class="hljs-name">clojurec-mode</span> . eglot-ensure)
         (<span class="hljs-name">clojurescript-mode</span> . eglot-ensure))
  <span class="hljs-symbol">:config</span>
  (<span class="hljs-name">setenv</span> <span class="hljs-string">"PATH"</span> (<span class="hljs-name">concat</span> <span class="hljs-string">"/usr/local/bin"</span> path-separator (<span class="hljs-name">getenv</span> <span class="hljs-string">"PATH"</span>)))
  (<span class="hljs-name">add-to-list</span> 'eglot-server-programs '(clojure-mode . (<span class="hljs-string">"clojure-lsp"</span>)))
  (<span class="hljs-name">add-to-list</span> 'eglot-server-programs '(clojurec-mode . (<span class="hljs-string">"clojure-lsp"</span>)))
  (<span class="hljs-name">add-to-list</span> 'eglot-server-programs '(clojurescript-mode . (<span class="hljs-string">"clojure-lsp"</span>)))
  (<span class="hljs-name">add-to-list</span> 'eglot-server-programs '(clojurex-mode . (<span class="hljs-string">"clojure-lsp"</span>))))
</code></pre>
<h3 id="heading-using-lsp-mode"><strong>Using</strong> <code>lsp-mode</code>:</h3>
<p>If you use <code>lsp-mode</code>, add the following configuration:</p>
<pre><code class="lang-lisp">(<span class="hljs-name">use-package</span> lsp-mode
  <span class="hljs-symbol">:ensure</span> <span class="hljs-literal">t</span>
  <span class="hljs-symbol">:hook</span> ((<span class="hljs-name">clojure-mode</span> . lsp)
         (<span class="hljs-name">clojurec-mode</span> . lsp)
         (<span class="hljs-name">clojurescript-mode</span> . lsp))
  <span class="hljs-symbol">:config</span>
  <span class="hljs-comment">;; add paths to your local installation of project mgmt tools, like lein</span>
  (<span class="hljs-name">setenv</span> <span class="hljs-string">"PATH"</span> (<span class="hljs-name">concat</span>
                   <span class="hljs-string">"/usr/local/bin"</span> path-separator
                   (<span class="hljs-name">getenv</span> <span class="hljs-string">"PATH"</span>)))
  (<span class="hljs-name">dolist</span> (<span class="hljs-name">m</span> '(clojure-mode
               clojurec-mode
               clojurescript-mode
               clojurex-mode))
     (<span class="hljs-name">add-to-list</span> 'lsp-language-id-configuration `(,m . <span class="hljs-string">"clojure"</span>)))
  (<span class="hljs-name">setq</span> lsp-clojure-server-command '(<span class="hljs-string">"/path/to/clojure-lsp"</span>))) <span class="hljs-comment">;; Optional: In case `clojure-lsp` is not in your $PATH</span>
</code></pre>
<blockquote>
<p>For further details, check the <a target="_blank" href="https://clojure-lsp.io/clients/#emacs"><strong>clojure-lsp Emacs client guide</strong></a>.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Alpine Linux on QEMU with Persistent Storage]]></title><description><![CDATA[This blog provides a comprehensive guide to setting up Alpine Linux on QEMU with persistent storage and SSH access.
Dependencies

Alpine Linux ISO

QEMU


Getting Started
1. Create a Virtual Disk
To enable persistent storage, create a virtual hard di...]]></description><link>https://blogs.9th.fun/alpine-linux-on-qemu-with-persistent-storage</link><guid isPermaLink="true">https://blogs.9th.fun/alpine-linux-on-qemu-with-persistent-storage</guid><category><![CDATA[AlpineLinux]]></category><category><![CDATA[QEMU]]></category><category><![CDATA[virtualization]]></category><category><![CDATA[Linux]]></category><category><![CDATA[ssh]]></category><dc:creator><![CDATA[Nosferatu]]></dc:creator><pubDate>Tue, 28 Jan 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p>This blog provides a comprehensive guide to setting up Alpine Linux on QEMU with persistent storage and SSH access.</p>
<h2 id="heading-dependencies"><strong>Dependencies</strong></h2>
<ol>
<li><p><a target="_blank" href="https://alpinelinux.org/downloads/">Alpine Linux ISO</a></p>
</li>
<li><p><a target="_blank" href="https://www.qemu.org/download/">QEMU</a></p>
</li>
</ol>
<h2 id="heading-getting-started"><strong>Getting Started</strong></h2>
<h3 id="heading-1-create-a-virtual-disk"><strong>1. Create a Virtual Disk</strong></h3>
<p>To enable persistent storage, create a virtual hard disk:</p>
<pre><code class="lang-bash">qemu-img create -f qcow2 alpine-persistent.qcow2 10G
</code></pre>
<h3 id="heading-2-run-qemu-with-iso-and-virtual-disk"><strong>2. Run QEMU with ISO and Virtual Disk</strong></h3>
<p>Boot Alpine Linux with the installation ISO and attach the virtual disk:</p>
<pre><code class="lang-bash">qemu-system-x86_64 \
  -m 1024 \
  -smp 2 \
  -boot d \
  -cdrom alpine-standard-3.21.2-x86_64.iso \
  -drive file=alpine-persistent.qcow2,format=qcow2 \
  -net nic -net user,hostfwd=tcp::2222-:22 \
  -display gtk
</code></pre>
<h3 id="heading-3-install-alpine-linux"><strong>3. Install Alpine Linux</strong></h3>
<ol>
<li><p>Login with the default credentials i.e. (root,&lt;no_passwd&gt;).</p>
</li>
<li><p>Start the setup script via <code>setup-alpine</code>.</p>
</li>
<li><p>When prompted to select a disk, choose sda (the attached virtual disk).</p>
</li>
<li><p>Follow the setup instructions and confirm writing changes to disk.</p>
</li>
<li><p>Once installation is complete, reboot the system.</p>
</li>
</ol>
<h3 id="heading-4-enable-ssh-optional"><strong>4. Enable SSH (Optional)</strong></h3>
<p>SSH allows remote access to your VM from the host machine. Start the ssh service and enable for future connections.</p>
<pre><code class="lang-bash">rc-service sshd start
rc-update add sshd
</code></pre>
<p>You can now connect via SSH using:</p>
<pre><code class="lang-bash">ssh -p 2222 root@localhost
</code></pre>
<h3 id="heading-5-boot-installed-alpine-linux"><strong>5. Boot Installed Alpine Linux</strong></h3>
<p>After installation, run QEMU without the ISO:</p>
<pre><code class="lang-bash">qemu-system-x86_64 \
  -m 1024 \
  -smp 2 \
  -drive file=alpine-persistent.qcow2,format=qcow2 \
  -net nic -net user,hostfwd=tcp::2222-:22 \
  -display gtk
</code></pre>
<blockquote>
<p>Now, your Alpine Linux VM is fully set up with persistent storage and SSH access.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Compiling nginx with HTTP3]]></title><description><![CDATA[This blog provides a step-by-step guide to compiling Nginx with HTTP/3 (QUIC) support. It covers installing OpenSSL 3.x, building Nginx from source, and configuring it to enable HTTP/3. The guide also includes instructions for setting up SSL certific...]]></description><link>https://blogs.9th.fun/compiling-nginx-with-http3</link><guid isPermaLink="true">https://blogs.9th.fun/compiling-nginx-with-http3</guid><category><![CDATA[nginx]]></category><category><![CDATA[http3]]></category><category><![CDATA[webserver]]></category><dc:creator><![CDATA[Nosferatu]]></dc:creator><pubDate>Mon, 09 Sep 2024 18:30:00 GMT</pubDate><content:encoded><![CDATA[<p>This blog provides a step-by-step guide to compiling Nginx with HTTP/3 (QUIC) support. It covers installing OpenSSL 3.x, building Nginx from source, and configuring it to enable HTTP/3. The guide also includes instructions for setting up SSL certificates, modifying Nginx’s configuration for QUIC, and verifying HTTP/3 functionality using Wireshark. This tutorial is designed for those looking to enhance their web server’s performance and security with the latest protocols.</p>
<h2 id="heading-install-openssl-3x-or-later"><strong>Install OpenSSL 3.x or Later</strong></h2>
<p>Nginx 1.26.2 comes bundled with OpenSSL 1.1.1f by default, which lacks QUIC support. Nginx’s QUIC support begins with OpenSSL 3.0. Download and extract OpenSSL 3.0.8, then proceed to build and install it.</p>
<pre><code class="lang-bash">wget https://www.openssl.org/<span class="hljs-built_in">source</span>/openssl-3.0.8.tar.gz
tar -xzf openssl-3.0.8.tar.gz
<span class="hljs-built_in">cd</span> openssl-3.0.8
./config
make
sudo make install
<span class="hljs-built_in">cd</span> ..
</code></pre>
<h2 id="heading-build-nginx-126x-with-openssl-3x"><strong>Build Nginx 1.26.x with OpenSSL 3.x</strong></h2>
<p>After downloading the Nginx source code, extract the contents and prepare for configuration.</p>
<pre><code class="lang-bash">wget http://nginx.org/download/nginx-1.26.2.tar.gz
tar -xzf nginx-1.26.2.tar.gz
<span class="hljs-built_in">cd</span> nginx-1.26.2
</code></pre>
<p>Rebuild Nginx using OpenSSL 3.x by specifying the appropriate configuration flags.</p>
<pre><code class="lang-bash">./configure \
    --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx \
    --modules-path=/usr/lib/nginx/modules \
    --conf-path=/etc/nginx/nginx.conf \
    --error-log-path=/var/<span class="hljs-built_in">log</span>/nginx/error.log \
    --http-log-path=/var/<span class="hljs-built_in">log</span>/nginx/access.log \
    --pid-path=/var/run/nginx.pid \
    --lock-path=/var/run/nginx.lock \
    --http-client-body-temp-path=/var/cache/nginx/client_temp \
    --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
    --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
    --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
    --http-scgi-temp-path=/var/cache/nginx/scgi_temp \
    --with-http_ssl_module --with-http_v2_module \
    --with-http_v3_module --with-openssl=../openssl-3.0.8
make
sudo make install
</code></pre>
<p>Verify the Nginx build configuration with <code>nginx -V</code>.</p>
<pre><code class="lang-plaintext">nginx version: nginx/1.26.2
built by gcc 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)
built with OpenSSL 3.0.8 7 Feb 2023
TLS SNI support enabled
configure arguments: \
    --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx \
    --modules-path=/usr/lib/nginx/modules \
    --conf-path=/etc/nginx/nginx.conf \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --pid-path=/var/run/nginx.pid \
    --lock-path=/var/run/nginx.lock \
    --http-client-body-temp-path=/var/cache/nginx/client_temp \
    --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
    --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
    --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
    --http-scgi-temp-path=/var/cache/nginx/scgi_temp \
    --with-http_ssl_module --with-http_v2_module \
    --with-http_v3_module --with-openssl=../openssl-3.0.8
</code></pre>
<h2 id="heading-edit-nginx-configuration"><strong>Edit nginx configuration</strong></h2>
<p>Modify the Nginx configuration file to enable HTTP/3 and set up the server’s SSL certificates. The configuration file is located at <code>/etc/nginx/nginx.conf</code>.</p>
<ul>
<li><p>Adjust the server block to listen on port 443 for both QUIC and SSL connections.</p>
</li>
<li><p>Specify the root directory and set up file paths for serving content, enabling auto-indexing.</p>
</li>
<li><p>Add the HTTP/3 Alt-Svc header to enable QUIC connections.</p>
</li>
</ul>
<pre><code class="lang-nginx"><span class="hljs-section">events</span> {
    <span class="hljs-attribute">worker_connections</span>  <span class="hljs-number">1024</span>;
}

<span class="hljs-section">http</span> {
    <span class="hljs-section">server</span> {
        <span class="hljs-attribute">listen</span> <span class="hljs-number">443</span> quic reuseport;
        <span class="hljs-attribute">listen</span> <span class="hljs-number">443</span> ssl;

        <span class="hljs-attribute">ssl_certificate</span>       &lt;ADD_YOUR_CRT&gt;;
        <span class="hljs-attribute">ssl_certificate_key</span>   &lt;ADD_YOUR_KEY&gt;;
        <span class="hljs-attribute">ssl_protocols</span> TLSv1 TLSv1.<span class="hljs-number">1</span> TLSv1.<span class="hljs-number">2</span> TLSv1.<span class="hljs-number">3</span>;

        <span class="hljs-attribute">root</span>                  &lt;ADD_YOUR_ROOT&gt;;
        <span class="hljs-attribute">index</span> index.html index.htm;

        <span class="hljs-attribute">location</span> / {
                <span class="hljs-attribute">root</span>          &lt;ADD_YOUR_ROOT&gt;;
                <span class="hljs-attribute">autoindex</span> <span class="hljs-literal">on</span>;
                <span class="hljs-attribute">try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ =<span class="hljs-number">404</span>;
        }

        <span class="hljs-attribute">add_header</span> alt-svc <span class="hljs-string">'h3=":443"; ma=86400'</span>;
    }
}
</code></pre>
<p>Save the changes, test the configuration, and start Nginx.</p>
<pre><code class="lang-bash">nginx -t
sudo systemctl start nginx
</code></pre>
<p>Verify HTTP/3 and QUIC functionality using Wireshark to capture and analyze network traffic, ensuring that the connection traces indicate QUIC support.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749804094052/5c024ea9-0b17-4799-aa4b-f5370b11cd1f.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>The IPs are masked for obvious reasons. But we can see traces of QUIC connection, which means http3 is working.</p>
</blockquote>
]]></content:encoded></item></channel></rss>