<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>reidel.dev — All Posts</title>
    <link>https://reidel.dev</link>
    <description>software dev, linux and open source</description>
    <language>en-us</language>
    <atom:link href="https://reidel.dev/feed.xml" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Mon, 01 Jun 2026 03:15:17 GMT</lastBuildDate>
    <copyright>CC BY 4.0 Joshua Reidel</copyright>

    <item>
      <title>Forgejo Setup</title>
      <link>https://reidel.dev/guides/forgejo-setup</link>
      <guid isPermaLink="true">https://reidel.dev/guides/forgejo-setup</guid>
      <pubDate>Sat, 09 May 2026 00:00:00 GMT</pubDate>
      <description>Creating a personal code repository isn&apos;t as hard as you may think now that options like Forgejo exist. To get started, you can follow this guide to create your own personal git forge.</description>
      <category>guides</category>
      <content:encoded><![CDATA[<h1>Forgejo Setup</h1>
<p>Creating a personal code repository isn't as hard as it seems now that options like Forgejo exist. To get started, you can follow this guide to create your own personal git forge.</p>
<h2>Server Setup</h2>
<p>I would recommend a server with at least 1GB RAM and 25GB storage for this.</p>
<p>Make sure to add a firewall rule to allow inbound TCP on port 2222. Set the allowlist on this port to your IP address only. This will keep things nice and secure for this entire setup process.</p>
<h2>Create a DNS Record</h2>
<p>It is handy to set up a <code>git</code> subdomain to access your forge via Forgejo's web client, that way in the future you can access your forge via https://git.yourdomain.com.</p>
<p>Go to whichever DNS management service you are using for your domain. It will likely be something like <strong>Manage Domains</strong> -&gt; select the DNS of the domain and add an A Record with the following values:</p>
<pre><code>Type: A
Host: git
Answer: add your server's public IP here
TTL: 600 (or default)
</code></pre>
<h2>Setup Proxy Server</h2>
<p>You will need to direct your <code>git</code> subdomain to the port Forgejo uses for its web client. A reverse proxy server can handle this nicely.</p>
<p>I used Caddy as a proxy server, you could also use Apache HTTP or nginx here as alternatives. Just make sure you are proxing your git domain to localhost:3000 so you can access the Forgejo web client later.</p>
<p>Edit Caddy file:</p>
<pre><code class="language-bash">sudo nano /etc/caddy/Caddyfile
</code></pre>
<p>Add this to the file content:</p>
<pre><code>git.yourdomain.com {
    reverse_proxy localhost:3000
}
</code></pre>
<p>Reload Caddy:</p>
<pre><code class="language-bash">sudo systemctl reload caddy
</code></pre>
<h2>Open Firewall for Git SSH</h2>
<p>Now you'll want to open a port for your git requests.</p>
<p>Run this on the server to allow external access to TCP port 2222:</p>
<pre><code class="language-bash">sudo ufw allow 2222/tcp
</code></pre>
<h2>Install and Configure Forgejo</h2>
<h3>1. Install Podman</h3>
<pre><code class="language-bash">sudo apt install podman -y
podman --version  # verify install
</code></pre>
<p>Enable the Podman socket:</p>
<pre><code class="language-bash">sudo loginctl enable-linger deploy
systemctl --user enable --now podman.socket
</code></pre>
<h3>2. Create Forgejo Directories</h3>
<pre><code class="language-bash">mkdir -p ~/forgejo/data
cd ~/forgejo
</code></pre>
<h3>3. Sanity Check Forgejo Container</h3>
<p>Add you own domain and timezone to the command below and run it to test the podman install.</p>
<pre><code class="language-bash">podman run -d \
  --name forgejo \
  --replace \
  -e USER_UID=$(id -u) \
  -e USER_GID=$(id -g) \
  -e FORGEJO__database__DB_TYPE=sqlite3 \
  -e FORGEJO__server__DOMAIN=git.yourdomain.com \
  -e FORGEJO__server__ROOT_URL=https://git.yourdomain.com/ \
  -e FORGEJO__server__SSH_DOMAIN=git.yourdomain.com \
  -e FORGEJO__service__DISABLE_REGISTRATION=true \
  -e TZ=America/Phoenix \
  -v ~/forgejo/data:/data \
  -v /etc/localtime:/etc/localtime:ro \
  -p 3000:3000 \
  -p 2222:22 \
  codeberg.org/forgejo/forgejo:9
</code></pre>
<p>Verify it is running:</p>
<pre><code class="language-bash">podman ps
curl -s http://localhost:3000 | head
</code></pre>
<p>Stop and remove the previous version that was running.</p>
<pre><code class="language-bash">podman stop forgejo
podman rm forgejo
</code></pre>
<p>Now we will install the real auto starting production server now that the test worked.</p>
<h3>4. Auto-start with systemd</h3>
<p>Create a directory for the Quadlet files:</p>
<pre><code class="language-bash">mkdir -p ~/.config/containers/systemd
</code></pre>
<p>Create the Forgejo container:</p>
<pre><code class="language-bash">nano ~/.config/containers/systemd/forgejo.container
</code></pre>
<p>Add this content to the file:</p>
<pre><code class="language-systemd">[Unit]
Description=Forgejo Git Server

[Container]
Image=codeberg.org/forgejo/forgejo:9
ContainerName=forgejo
Environment=USER_UID=YOUR_UID
Environment=USER_GID=YOU_GUID
Environment=FORGEJO__database__DB_TYPE=sqlite3
Environment=FORGEJO__server__DOMAIN=git.yourdomain.com
Environment=FORGEJO__server__ROOT_URL=https://git.yourdomain.com/
Environment=FORGEJO__server__SSH_DOMAIN=git.yourdomain.com
Environment=FORGEJO__service__DISABLE_REGISTRATION=true
Environment=TZ=America/Phoenix
Volume=/home/deploy/forgejo/data:/data
Volume=/etc/localtime:/etc/localtime:ro
PublishPort=3000:3000
PublishPort=2222:22

[Install]
WantedBy=default.target
</code></pre>
<p>Now you should edit the values for your own setup. Start by changing the YOUR_UID and YOUR_GID values.</p>
<p>Verify your user ID values:</p>
<pre><code class="language-bash">id -u &lt;username&gt;  # should output 1000
id -g &lt;username&gt;  # should output 1000
</code></pre>
<p>Then replace the YOUR_UID/YOUR_GID value in the settings below with your <code>id</code> values. This should be 1000 on most new systems.</p>
<p>More changes you should make here:</p>
<ul>
<li>Add your actual domain name instead of <code>git.yourdomain.com</code>.</li>
<li>Change the timezone <code>Environment=TZ</code> to your actual timezone.</li>
</ul>
<p>Note: In a rootless Podman setup, app.ini will be owned by a remapped UID. On a single-user VPS this is acceptable. If you're sharing the server with other users, consider the sensitivity of the file.</p>
<h3>4. Start Forgejo</h3>
<pre><code class="language-bash">systemctl --user daemon-reload
systemctl --user start forgejo
</code></pre>
<p>Verify it is running:</p>
<pre><code class="language-bash">systemctl --user status forgejo
</code></pre>
<p>Check if Forgejo is responding:</p>
<pre><code class="language-bash">curl -s http://localhost:3000 | head
</code></pre>
<p>View logs:</p>
<pre><code class="language-bash">journalctl --user -u forgejo
</code></pre>
<p>Afterward you can manage it like any other service:</p>
<pre><code class="language-bash">systemctl --user status forgejo
systemctl --user restart forgejo
systemctl --user logs forgejo
</code></pre>
<p>If all is well, continue on to complete the wizard.</p>
<h3>6. Complete Forgejo Config Wizard</h3>
<p>Open the URL to see the Forgejo config wizard: https://git.yourdomain.com/</p>
<p>Use the default options. Add an Administrator account and create the instance. Then navigate to the URL again to see the interface for Forgejo.</p>
<h3>7. Hardening Forgejo</h3>
<p>For a private forge, you'll want to disable any other user from registering. Confirm the DISABLE_REGISTRATION value below is present in app.ini, it should have been written on first run, but it's worth verifying. This should block all users from signing up, so only your account will be present on the forge.</p>
<p>Open the config:</p>
<pre><code class="language-bash">sudo nano ~/forgejo/data/gitea/conf/app.ini
</code></pre>
<p>Make sure these are set:</p>
<pre><code class="language-ini">[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = true

[openid]
ENABLE_OPENID_SIGNIN = false
ENABLE_OPENID_SIGNUP = false

[api]
ENABLE_SWAGGER = false
</code></pre>
<p>Restart Forgejo:</p>
<pre><code class="language-bash">systemctl --user restart forgejo
</code></pre>
<p>It should be working now.</p>
<h4>Setup up 2-factor auth</h4>
<p>At this point it is a good idea to go into the Forgejo web client at https://git.yourdomain.com/ and set up 2-factor authentication.</p>
<p>Navigate to User Settings → Security → Two-Factor Authentication.</p>
<h3>8. Add SSH key to Forgejo</h3>
<p>Add your SSH key like you would in Codeberg/GitHub by pasting the key from <code>~/.ssh/id_ed25519.pub</code> into the Forgejo Web Client under &quot;SSH Keys &gt; Manage SSH Keys&quot;</p>
<p>Add a local entry to the SSH config at <code>~/.ssh/config</code>:</p>
<pre><code>Host forgejo
    HostName git.yourdomain.com
    Port 2222
    User git
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes
</code></pre>
<p>Now you can use the shorthand <code>forgejo</code> for git requests.</p>
<h2>Basic usage</h2>
<p>Clone repo:</p>
<pre><code class="language-bash">git clone forgejo:youruser/repo.git
</code></pre>
<p>Push an existing project:</p>
<pre><code class="language-bash">cd your-project
git remote add forgejo forgejo:youruser/repo.git
git push forgejo main
</code></pre>
<p>You may be used to copying the ssh from the web client like you might in Github or Codeberg, but here that won't work, since it uses port 22 by default and not port 2222 with a special ssh config.</p>
<p>It can work that way, but it requires the port to be specified:</p>
<pre><code class="language-bash">git clone ssh://git@git.yourdomain.com:2222/youruser/repo.git
</code></pre>
<p>That is it, you now should have a working private git forge to host all your coding projects.</p>
<p>For more information see <a href="https://forgejo.org/docs/latest/admin/installation/">Forgejo Install Docs</a></p>
]]></content:encoded>
    </item>

    <item>
      <title>Miniflux Server Setup</title>
      <link>https://reidel.dev/guides/miniflux-setup</link>
      <guid isPermaLink="true">https://reidel.dev/guides/miniflux-setup</guid>
      <pubDate>Tue, 21 Apr 2026 00:00:00 GMT</pubDate>
      <description>Miniflux is a minimalist RSS web client you can set up on a personal server.</description>
      <category>guides</category>
      <content:encoded><![CDATA[<h1>Miniflux Server Setup</h1>
<p><em>April 21, 2026</em></p>
<hr>
<p>Miniflux is a minimalist RSS web client you can set up on a personal server.</p>
<p>It's pretty neat to use since it allows you to create a single RSS interface you control that you can access from any device. No more managing multiple RSS clients on various devices. No more syncing or manually managing your RSS subscription files.</p>
<p>You can find its homepage here <a href="https://miniflux.app/">https://miniflux.app/</a></p>
<p>Below is a simple guide for setting it up on a new Linux instance.</p>
<h2>Requirements</h2>
<ul>
<li>Linux distro</li>
<li>Podman</li>
<li>Proxy web server</li>
</ul>
<p>I used Debian, Podman, and Caddy setup on a Digital Ocean Droplet. You can use alternatives if you wish, just keep in mind some configuration steps may be different.</p>
<blockquote>
<p>This guide uses Podman and its Quadlet systemd integration. If you prefer Docker, the container images and environment variables are the same, but you'll want a docker-compose.yml instead of Quadlets.</p>
</blockquote>
<h2>Configuring Miniflux</h2>
<p><strong>1. Create directories for Miniflux</strong></p>
<pre><code class="language-bash">mkdir -p ~/miniflux/postgres-data
</code></pre>
<p><strong>2. Create the network Quadlet:</strong></p>
<pre><code class="language-bash">nano ~/.config/containers/systemd/miniflux.network
</code></pre>
<p>Add this content:</p>
<pre><code class="language-systemd">[Network]
NetworkName=miniflux
</code></pre>
<p><strong>3. Create the Postgres Quadlet</strong></p>
<pre><code class="language-bash">nano ~/.config/containers/systemd/miniflux-db.container
</code></pre>
<p>Add this content:</p>
<pre><code class="language-systemd">[Unit]
Description=Miniflux PostgreSQL Database

[Container]
Image=docker.io/library/postgres:15
ContainerName=miniflux-db
Environment=POSTGRES_USER=miniflux
Environment=POSTGRES_PASSWORD=CHANGE_THIS_TO_SOMETHING_RANDOM
Environment=POSTGRES_DB=miniflux
Volume=/home/deploy/miniflux/postgres-data:/var/lib/postgresql/data
Network=miniflux.network

[Install]
WantedBy=default.target
</code></pre>
<p><strong>4. Create the Miniflux Quadlet</strong></p>
<pre><code class="language-bash">nano ~/.config/containers/systemd/miniflux.container
</code></pre>
<p>Add this content (then edit it as recommended below):</p>
<pre><code class="language-systemd">[Unit]
Description=Miniflux RSS Reader
After=miniflux-db.service
Requires=miniflux-db.service

[Container]
Image=docker.io/miniflux/miniflux:latest
ContainerName=miniflux
Environment=DATABASE_URL=postgres://miniflux:CHANGE_THIS_TO_SOMETHING_RANDOM@miniflux-db/miniflux?sslmode=disable
Environment=RUN_MIGRATIONS=1
Environment=CREATE_ADMIN=1
Environment=ADMIN_USERNAME=ADD_YOUR_ADMIN_USERNAME
Environment=ADMIN_PASSWORD=ADD_YOUR_ADMIN_PASSWORD
PublishPort=8080:8080
Network=miniflux.network

[Install]
WantedBy=default.target
</code></pre>
<p>Make sure the DATABASE_URL password CHANGE_THIS_TO_SOMETHING_RANDOM in the two Quadlet files is changed to something that matches. ONLY use alphanumeric characters here, it can't handle anything else since special characters break URL parsing.</p>
<p>I recommend using a password generator to create a random alphanumeric string at least 30 characters long then replace CHANGE_THIS_TO_SOMETHING_RANDOM in both Postgres and Miniflux Quadlet files.</p>
<p>Change the admin username and password, these will be your login credentials in the web interface for the first time login. Once logged in for the first time, you will create new credentials and remove these entries.</p>
<p>Make sure to save the admin credentials to your password manager.</p>
<p><strong>5. Pull images and start everything</strong></p>
<pre><code class="language-bash">podman pull docker.io/library/postgres:15
podman pull docker.io/miniflux/miniflux:latest
systemctl --user daemon-reload
systemctl --user start miniflux-db
systemctl --user start miniflux
</code></pre>
<p>Verify it is running:</p>
<pre><code class="language-bash">systemctl --user status miniflux-db
systemctl --user status miniflux
</code></pre>
<blockquote>
<p><strong>Note:</strong> This guide uses <code>systemctl --user</code> to manage services as the <code>deploy</code> user. For these services to start automatically on boot, linger must be enabled for that user (`loginctl enable-linger deploy).</p>
</blockquote>
<h2>Setup DNS for an RSS subdomain</h2>
<p>Add an A record in your domain registrar to point to your RSS subdomain, for example <code>rss.yourdomain.com</code></p>
<p>Update the proxy server to point the subdomain at your local Miniflux.</p>
<p>In my case, I used Caddy to point the subdomain to the local Miniflux instance.</p>
<pre><code class="language-bash">sudo nano /etc/caddy/Caddyfile
</code></pre>
<p>Add alongside your existing blocks:</p>
<pre><code>rss.yourdomain.com {
    reverse_proxy localhost:8080
}
</code></pre>
<p>Reload the proxy server:</p>
<pre><code class="language-bash">sudo systemctl reload caddy
</code></pre>
<h2>Testing and Cleanup</h2>
<p>Visit your domain such as https://rss.yourdomain.com and log in with the admin credentials you set.</p>
<p>Before proceeding, go to Settings → Change Username/Password to update your admin credentials. Do not skip this.</p>
<p><strong>1. Clean up bootstrap env vars</strong></p>
<p>Once you've confirmed login works, edit the Miniflux Quadlet and remove the three bootstrap lines:</p>
<pre><code class="language-bash">nano ~/.config/containers/systemd/miniflux.container
</code></pre>
<p>Remove:</p>
<pre><code class="language-systemd">Environment=CREATE_ADMIN=1
Environment=ADMIN_USERNAME=ADD_YOUR_ADMIN_USERNAME
Environment=ADMIN_PASSWORD=ADD_YOUR_ADMIN_PASSWORD
</code></pre>
<p>Then:</p>
<pre><code class="language-bash">systemctl --user daemon-reload
systemctl --user restart miniflux
</code></pre>
<p><strong>2. Import your RSS feeds from other sources</strong></p>
<p>I previously used Newsboat, so I exported my local Newsboat feed so I could import into Miniflux:</p>
<pre><code class="language-bash">newsboat -e &gt; feeds.opml
</code></pre>
<p>Then in the Miniflux web UI, go to Settings → Import and upload the OPML file.</p>
<p>That's the whole thing, now you have a single centralized RSS feed you can access and update from all your devices.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>