<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>security &amp;mdash; saint</title>
    <link>https://avys.group.lt/saint/tag:security</link>
    <description>scrapbook of a sysadmin</description>
    <pubDate>Fri, 01 May 2026 11:00:57 +0000</pubDate>
    <item>
      <title>Upgrading Mastodon to v4.5.1: A Journey in Automation and Security</title>
      <link>https://avys.group.lt/saint/upgrading-mastodon-to-v4-5-1-a-journey-in-automation-and-security</link>
      <description>&lt;![CDATA[Published: November 13, 2025&#xA;Author: River Instance Team&#xA;Reading Time: 8 minutes&#xA;&#xA;---&#xA;&#xA;The Mission&#xA;&#xA;Today we upgraded our Mastodon instance (river.group.lt) from version 4.5.0 to 4.5.1. While this might sound like a routine patch update, we used it as an opportunity to make our infrastructure more secure and our deployment process more automated. Here&#39;s what we learned along the way.&#xA;&#xA;---&#xA;&#xA;Why Upgrade?&#xA;&#xA;When glitch-soc (our preferred Mastodon variant) released version 4.5.1, we reviewed the changelog and found 10 bug fixes, including:&#xA;&#xA;Better keyboard navigation in the Alt text modal&#xA;Fixed issues with quote posts appearing as &#34;unquotable&#34;&#xA;Improved filter application in detailed views&#xA;Build fixes for ARM64 architecture&#xA;&#xA;More importantly: no database migrations, no breaking changes, and no new features that could introduce instability. This is what we call a &#34;safe upgrade&#34; - the perfect candidate for improving our processes while updating.&#xA;&#xA;---&#xA;&#xA;The Starting Point&#xA;&#xA;Our Mastodon setup isn&#39;t quite standard. We run:&#xA;&#xA;glitch-soc variant (Mastodon fork with extra features)&#xA;Custom Docker images with Sentry monitoring baked in&#xA;Kubernetes deployment via Helm charts&#xA;AMD64 architecture (important for cross-platform builds)&#xA;&#xA;This means we can&#39;t just pull the latest official image - we need to rebuild our custom images with each new version.&#xA;&#xA;---&#xA;&#xA;The Problem We Solved&#xA;&#xA;Before this upgrade, our build process looked like this:&#xA;&#xA;Find Harbor registry credentials (where?)&#xA;Copy-paste username and password&#xA;docker login registry.example.com&#xA;Enter credentials manually&#xA;Update version in 4 different files&#xA;Hope they all match&#xA;./build.sh&#xA;Wait for builds to complete&#xA;Manually verify everything worked&#xA;&#xA;The issues:&#xA;Credentials stored in shell history (security risk)&#xA;Manual steps prone to typos&#xA;No automation = easy to forget steps&#xA;Credentials sitting in ~/.docker/config.json unencrypted&#xA;&#xA;We knew we could do better.&#xA;&#xA;---&#xA;&#xA;The Solution: Infisical Integration&#xA;&#xA;Infisical is a secrets management platform - think of it as a secure vault for credentials that your applications can access automatically. Instead of storing Harbor registry credentials on our laptop, we:&#xA;&#xA;Stored credentials in Infisical (one-time setup)&#xA;Updated our build script to fetch credentials automatically&#xA;Automated the Docker login process&#xA;&#xA;Now our build script looks like this:&#xA;&#xA;!/bin/bash&#xA;set -e&#xA;&#xA;VERSION=&#34;v4.5.1&#34;&#xA;REGISTRY=&#34;registry.example.com/library&#34;&#xA;PROJECTID=&#34;your-infisical-project-id&#34;&#xA;&#xA;echo &#34;🔑 Logging in to Harbor registry...&#34;&#xA;Fetch credentials from Infisical&#xA;HARBORUSERNAME=$(infisical secrets get \&#xA;  --domain https://secrets.example.com/api \&#xA;  --projectId ${PROJECTID} \&#xA;  --env prod HARBORUSERNAME \&#xA;  --silent -o json | jq -r &#39;.[0].secretValue&#39;)&#xA;&#xA;HARBORPASSWORD=$(infisical secrets get \&#xA;  --domain https://secrets.example.com/api \&#xA;  --projectId ${PROJECTID} \&#xA;  --env prod HARBORPASSWORD \&#xA;  --silent -o json | jq -r &#39;.[0].secretValue&#39;)&#xA;&#xA;Automatic login&#xA;echo &#34;${HARBORPASSWORD}&#34; | docker login ${REGISTRY} \&#xA;  --username &#34;${HARBORUSERNAME}&#34; --password-stdin&#xA;&#xA;Build and push images...&#xA;&#xA;  Note: Code examples use placeholder values. Replace registry.example.com, secrets.example.com, and your-infisical-project-id with your actual infrastructure endpoints.&#xA;&#xA;The benefits:&#xA;✅ No credentials in shell history&#xA;✅ No manual copy-pasting&#xA;✅ Audit trail of when credentials were accessed&#xA;✅ Easy credential rotation&#xA;✅ Works the same on any machine with Infisical access&#xA;&#xA;---&#xA;&#xA;The Upgrade Process&#xA;&#xA;With our improved automation in place, the actual upgrade was straightforward:&#xA;&#xA;Step 1: Research&#xA;&#xA;We used AI assistance to research the glitch-soc v4.5.1 release:&#xA;Confirmed it was a patch release (low risk)&#xA;Verified no database migrations required&#xA;Reviewed all 10 bug fixes&#xA;Checked for breaking changes (none found)&#xA;&#xA;Lesson: Always research before executing. 15 minutes of reading can prevent hours of rollback.&#xA;&#xA;Step 2: Update Version References&#xA;&#xA;We needed to update the version in exactly 4 places:&#xA;&#xA;docker-assets/build.sh - Build script version variable&#xA;docker-assets/Dockerfile.mastodon-sentry - Base image version&#xA;docker-assets/Dockerfile.streaming-sentry - Streaming image version&#xA;values-river.yaml - Helm values for both image tags&#xA;&#xA;Lesson: Keep a checklist of version locations. It&#39;s easy to miss one.&#xA;&#xA;Step 3: Build Custom Images&#xA;&#xA;cd docker-assets&#xA;./build.sh&#xA;&#xA;The script now:&#xA;Fetches credentials from Infisical ✓&#xA;Logs into Harbor registry ✓&#xA;Builds both images with --platform linux/amd64 ✓&#xA;Pushes to registry ✓&#xA;Provides clear success/failure messages ✓&#xA;&#xA;Build time: ~5 seconds (thanks to Docker layer caching!)&#xA;&#xA;Step 4: Deploy to Kubernetes&#xA;&#xA;cd ..&#xA;helm upgrade river-mastodon . -n mastodon -f values-river.yaml&#xA;&#xA;Helm performed a rolling update:&#xA;Old pods kept running while new ones started&#xA;New pods pulled v4.5.1 images&#xA;Old pods terminated once new ones were healthy&#xA;Zero downtime for our users&#xA;&#xA;Step 5: Verify&#xA;&#xA;kubectl exec -n mastodon deployment/river-mastodon-web -- tootctl version&#xA;Output: 4.5.1+glitch&#xA;&#xA;All three pod types (web, streaming, sidekiq) now running the new version. Success! 🎉&#xA;&#xA;---&#xA;&#xA;What We Learned&#xA;&#xA;1. Automation Compounds Over Time&#xA;&#xA;The Infisical integration took about 60 minutes to implement. The actual version bump took 30 minutes. That might seem like overkill for a &#34;simple&#34; upgrade.&#xA;&#xA;But here&#39;s the math:&#xA;Manual process: 5 minutes per build to manage credentials&#xA;Automated process: 0 minutes&#xA;Builds per year: ~20 upgrades and tests&#xA;Time saved annually: 100 minutes&#xA;Payback period: 12 builds (~6 months)&#xA;&#xA;Plus, we eliminated a security risk. The real value isn&#39;t just time - it&#39;s confidence and safety.&#xA;&#xA;2. Separate Upstream from Custom&#xA;&#xA;We keep the upstream Helm chart (Chart.yaml) completely untouched. Our customizations live in:&#xA;Custom Dockerfiles (add Sentry)&#xA;Values overrides (values-river.yaml)&#xA;Build scripts&#xA;&#xA;Why this matters: We can pull upstream chart updates without conflicts. Our changes are additive, not modifications.&#xA;&#xA;3. Test Incrementally&#xA;&#xA;We didn&#39;t just run the full build and hope it worked. We tested:&#xA;&#xA;✓ Credential retrieval from Infisical&#xA;✓ JSON parsing with jq&#xA;✓ Docker login with retrieved credentials&#xA;✓ Image builds&#xA;✓ Image pushes to registry&#xA;✓ Kubernetes deployment&#xA;✓ Running version verification&#xA;&#xA;Each step validated before moving forward. When something broke (initial credential permissions), we caught it immediately.&#xA;&#xA;4. Documentation Is for Future You&#xA;&#xA;We wrote a comprehensive retrospective covering:&#xA;What went well&#xA;What we learned&#xA;What we&#39;d do differently next time&#xA;Troubleshooting guides for common issues&#xA;&#xA;In 6 months when we upgrade to v4.6.0, we&#39;ll thank ourselves for this documentation.&#xA;&#xA;5. Version Numbers Tell a Story&#xA;&#xA;Understanding semantic versioning helps assess risk:&#xA;&#xA;v4.5.0 → v4.5.1 = Patch release (bug fixes only, low risk)&#xA;v4.5.x → v4.6.0 = Minor release (new features, moderate risk)&#xA;v4.x.x → v5.0.0 = Major release (breaking changes, high risk)&#xA;&#xA;This informed our decision to proceed quickly with minimal testing.&#xA;&#xA;---&#xA;&#xA;What We&#39;d Do Differently Next Time&#xA;&#xA;Despite the success, we identified improvements:&#xA;&#xA;High Priority&#xA;&#xA;1. Validate credentials before building&#xA;&#xA;Currently, we discover authentication failures during the image push (after building). Better:&#xA;&#xA;Test login BEFORE building&#xA;if ! docker login ...; then&#xA;  echo &#34;❌ Auth failed&#34;&#xA;  exit 1&#xA;fi&#xA;&#xA;2. Initialize Infisical project config&#xA;&#xA;Running infisical init in the project directory creates a .infisical.json file, eliminating the need for --projectId flags in every command.&#xA;&#xA;3. Add version consistency checks&#xA;&#xA;A simple script to verify all 4 files have matching versions before building would catch human errors.&#xA;&#xA;Medium Priority&#xA;&#xA;4. Automated deployment verification&#xA;&#xA;Replace manual kubectl checks with a script that:&#xA;Waits for pods to be ready&#xA;Extracts running version&#xA;Compares to expected version&#xA;Reports success/failure&#xA;&#xA;5. Dry-run mode for build script&#xA;&#xA;Test the script logic without actually building or pushing images. Useful for testing changes to the script itself.&#xA;&#xA;---&#xA;&#xA;The Impact&#xA;&#xA;Before this session:&#xA;Manual credential management&#xA;5+ minutes per build for login&#xA;Credentials in shell history (security risk)&#xA;No audit trail&#xA;&#xA;After this session:&#xA;Automated credential retrieval&#xA;0 minutes per build for login&#xA;Credentials never exposed (security improvement)&#xA;Full audit trail in Infisical&#xA;Repeatable process documented&#xA;&#xA;Plus: We&#39;re running Mastodon v4.5.1 with 10 bug fixes, making our instance more stable for our users.&#xA;&#xA;---&#xA;&#xA;Lessons for Other Mastodon Admins&#xA;&#xA;If you run a Mastodon instance, here&#39;s what we learned that might help you:&#xA;&#xA;For Small Instances&#xA;&#xA;Even if you&#39;re running standard Mastodon without customizations:&#xA;&#xA;Document your upgrade process - Your future self will thank you&#xA;Test in staging first - If you don&#39;t have staging, test with dry-run/simulation&#xA;Always check release notes - 5 minutes of reading prevents hours of debugging&#xA;Use semantic versioning to assess risk - Patch releases are usually safe&#xA;&#xA;For Custom Deployments&#xA;&#xA;If you run custom images like we do:&#xA;&#xA;Separate upstream from custom - Keep modifications isolated and additive&#xA;Automate credential management - Shell history is not secure storage&#xA;Use Docker layer caching - Speeds up builds dramatically&#xA;Platform flags matter - --platform linux/amd64 if deploying to different architecture&#xA;Verify the running version - Don&#39;t assume deployment worked, check it&#xA;&#xA;For Kubernetes Deployments&#xA;&#xA;If you deploy to Kubernetes:&#xA;&#xA;Rolling updates are your friend - Zero downtime is achievable&#xA;Helm revisions enable easy rollback - helm rollback is simple and fast&#xA;Verify pod image versions - Check what&#39;s actually running, not just deployed&#xA;Monitor during rollout - Watch pod status, don&#39;t just fire and forget&#xA;&#xA;---&#xA;&#xA;The Numbers&#xA;&#xA;Session Duration: 90 minutes total&#xA;Research: 15 minutes&#xA;Version updates: 10 minutes&#xA;Infisical integration: 60 minutes&#xA;Build &amp; deploy: 5 minutes&#xA;&#xA;Deployment Stats:&#xA;Downtime: 0 seconds (rolling update)&#xA;Pods affected: 3 (web, streaming, sidekiq)&#xA;Helm revision: 166&#xA;Rollback complexity: Low (single command)&#xA;&#xA;Lines of code changed: 18 lines across 4 files&#xA;Lines of documentation written: 629 lines (retrospective)&#xA;Security improvements: 1 major (credential management)&#xA;&#xA;---&#xA;&#xA;Final Thoughts&#xA;&#xA;What started as a simple patch upgrade turned into a significant infrastructure improvement. The version bump was almost trivial - the real work was automating away manual steps and eliminating security risks.&#xA;&#xA;This is what good ops work looks like: using routine maintenance as an opportunity to make systems better. The 60 minutes we spent on Infisical integration will pay dividends on every future build. The documentation we wrote will help the next person (or future us) upgrade with confidence.&#xA;&#xA;Mastodon v4.5.1 is running smoothly, our build process is more secure, and we learned lessons that will make the next upgrade even smoother.&#xA;&#xA;---&#xA;&#xA;Resources&#xA;&#xA;For Mastodon Admins:&#xA;Mastodon Upgrade Documentation&#xA;glitch-soc Releases&#xA;&#xA;For Infrastructure:&#xA;Infisical (Secrets Management)&#xA;Docker Build Best Practices&#xA;Helm Upgrade Documentation&#xA;&#xA;Our Instance:&#xA;river.group.lt - Live Mastodon instance&#xA;Running glitch-soc v4.5.1+glitch&#xA;Kubernetes + Helm deployment&#xA;Custom images with Sentry monitoring&#xA;&#xA;---&#xA;&#xA;Questions?&#xA;&#xA;If you&#39;re running a Mastodon instance and have questions about:&#xA;Upgrading glitch-soc variants&#xA;Custom Docker image workflows&#xA;Kubernetes deployments&#xA;Secrets management with Infisical&#xA;Zero-downtime upgrades&#xA;&#xA;Feel free to reach out! We&#39;re happy to share what we&#39;ve learned.&#xA;&#xA;---&#xA;&#xA;Tags: #mastodon #glitch-soc #kubernetes #devops #infrastructure #security #automation&#xA;&#xA;---&#xA;&#xA;This blog post is part of our infrastructure documentation series. We believe in sharing knowledge to help others running similar systems. All technical details are from our actual upgrade session on November 13, 2025.&#xA;&#xA;Comment in the Fediverse @saint@river.group.lt]]&gt;</description>
      <content:encoded><![CDATA[<p><strong>Published:</strong> November 13, 2025
<strong>Author:</strong> River Instance Team
<strong>Reading Time:</strong> 8 minutes</p>

<hr>

<h2 id="the-mission">The Mission</h2>

<p>Today we upgraded our Mastodon instance (river.group.lt) from version 4.5.0 to 4.5.1. While this might sound like a routine patch update, we used it as an opportunity to make our infrastructure more secure and our deployment process more automated. Here&#39;s what we learned along the way.</p>

<hr>

<h2 id="why-upgrade">Why Upgrade?</h2>

<p>When glitch-soc (our preferred Mastodon variant) released version 4.5.1, we reviewed the changelog and found 10 bug fixes, including:</p>
<ul><li>Better keyboard navigation in the Alt text modal</li>
<li>Fixed issues with quote posts appearing as “unquotable”</li>
<li>Improved filter application in detailed views</li>
<li>Build fixes for ARM64 architecture</li></ul>

<p>More importantly: no database migrations, no breaking changes, and no new features that could introduce instability. This is what we call a “safe upgrade” – the perfect candidate for improving our processes while updating.</p>

<hr>

<h2 id="the-starting-point">The Starting Point</h2>

<p>Our Mastodon setup isn&#39;t quite standard. We run:</p>
<ul><li><strong>glitch-soc variant</strong> (Mastodon fork with extra features)</li>
<li><strong>Custom Docker images</strong> with Sentry monitoring baked in</li>
<li><strong>Kubernetes deployment</strong> via Helm charts</li>
<li><strong>AMD64 architecture</strong> (important for cross-platform builds)</li></ul>

<p>This means we can&#39;t just pull the latest official image – we need to rebuild our custom images with each new version.</p>

<hr>

<h2 id="the-problem-we-solved">The Problem We Solved</h2>

<p>Before this upgrade, our build process looked like this:</p>

<pre><code class="language-bash"># Find Harbor registry credentials (where?)
# Copy-paste username and password
docker login registry.example.com
# Enter credentials manually
# Update version in 4 different files
# Hope they all match
./build.sh
# Wait for builds to complete
# Manually verify everything worked
</code></pre>

<p><strong>The issues:</strong>
– Credentials stored in shell history (security risk)
– Manual steps prone to typos
– No automation = easy to forget steps
– Credentials sitting in <code>~/.docker/config.json</code> unencrypted</p>

<p>We knew we could do better.</p>

<hr>

<h2 id="the-solution-infisical-integration">The Solution: Infisical Integration</h2>

<p><a href="https://infisical.com/" rel="nofollow">Infisical</a> is a secrets management platform – think of it as a secure vault for credentials that your applications can access automatically. Instead of storing Harbor registry credentials on our laptop, we:</p>
<ol><li><strong>Stored credentials in Infisical</strong> (one-time setup)</li>
<li><strong>Updated our build script</strong> to fetch credentials automatically</li>
<li><strong>Automated the Docker login</strong> process</li></ol>

<p>Now our build script looks like this:</p>

<pre><code class="language-bash">#!/bin/bash
set -e

VERSION=&#34;v4.5.1&#34;
REGISTRY=&#34;registry.example.com/library&#34;
PROJECT_ID=&#34;&lt;your-infisical-project-id&gt;&#34;

echo &#34;🔑 Logging in to Harbor registry...&#34;
# Fetch credentials from Infisical
HARBOR_USERNAME=$(infisical secrets get \
  --domain https://secrets.example.com/api \
  --projectId ${PROJECT_ID} \
  --env prod HARBOR_USERNAME \
  --silent -o json | jq -r &#39;.[0].secretValue&#39;)

HARBOR_PASSWORD=$(infisical secrets get \
  --domain https://secrets.example.com/api \
  --projectId ${PROJECT_ID} \
  --env prod HARBOR_PASSWORD \
  --silent -o json | jq -r &#39;.[0].secretValue&#39;)

# Automatic login
echo &#34;${HARBOR_PASSWORD}&#34; | docker login ${REGISTRY} \
  --username &#34;${HARBOR_USERNAME}&#34; --password-stdin

# Build and push images...
</code></pre>

<blockquote><p><strong>Note:</strong> Code examples use placeholder values. Replace <code>registry.example.com</code>, <code>secrets.example.com</code>, and <code>&lt;your-infisical-project-id&gt;</code> with your actual infrastructure endpoints.</p></blockquote>

<p><strong>The benefits:</strong>
– ✅ No credentials in shell history
– ✅ No manual copy-pasting
– ✅ Audit trail of when credentials were accessed
– ✅ Easy credential rotation
– ✅ Works the same on any machine with Infisical access</p>

<hr>

<h2 id="the-upgrade-process">The Upgrade Process</h2>

<p>With our improved automation in place, the actual upgrade was straightforward:</p>

<h3 id="step-1-research">Step 1: Research</h3>

<p>We used AI assistance to research the glitch-soc v4.5.1 release:
– Confirmed it was a patch release (low risk)
– Verified no database migrations required
– Reviewed all 10 bug fixes
– Checked for breaking changes (none found)</p>

<p><strong>Lesson:</strong> Always research before executing. 15 minutes of reading can prevent hours of rollback.</p>

<h3 id="step-2-update-version-references">Step 2: Update Version References</h3>

<p>We needed to update the version in exactly 4 places:</p>
<ol><li><code>docker-assets/build.sh</code> – Build script version variable</li>
<li><code>docker-assets/Dockerfile.mastodon-sentry</code> – Base image version</li>
<li><code>docker-assets/Dockerfile.streaming-sentry</code> – Streaming image version</li>
<li><code>values-river.yaml</code> – Helm values for both image tags</li></ol>

<p><strong>Lesson:</strong> Keep a checklist of version locations. It&#39;s easy to miss one.</p>

<h3 id="step-3-build-custom-images">Step 3: Build Custom Images</h3>

<pre><code class="language-bash">cd docker-assets
./build.sh
</code></pre>

<p>The script now:
– Fetches credentials from Infisical ✓
– Logs into Harbor registry ✓
– Builds both images with <code>--platform linux/amd64</code> ✓
– Pushes to registry ✓
– Provides clear success/failure messages ✓</p>

<p>Build time: ~5 seconds (thanks to Docker layer caching!)</p>

<h3 id="step-4-deploy-to-kubernetes">Step 4: Deploy to Kubernetes</h3>

<pre><code class="language-bash">cd ..
helm upgrade river-mastodon . -n mastodon -f values-river.yaml
</code></pre>

<p>Helm performed a rolling update:
– Old pods kept running while new ones started
– New pods pulled v4.5.1 images
– Old pods terminated once new ones were healthy
– <strong>Zero downtime</strong> for our users</p>

<h3 id="step-5-verify">Step 5: Verify</h3>

<pre><code class="language-bash">kubectl exec -n mastodon deployment/river-mastodon-web -- tootctl version
# Output: 4.5.1+glitch
</code></pre>

<p>All three pod types (web, streaming, sidekiq) now running the new version. Success! 🎉</p>

<hr>

<h2 id="what-we-learned">What We Learned</h2>

<h3 id="1-automation-compounds-over-time">1. Automation Compounds Over Time</h3>

<p>The Infisical integration took about 60 minutes to implement. The actual version bump took 30 minutes. That might seem like overkill for a “simple” upgrade.</p>

<p>But here&#39;s the math:
– <strong>Manual process:</strong> 5 minutes per build to manage credentials
– <strong>Automated process:</strong> 0 minutes
– <strong>Builds per year:</strong> ~20 upgrades and tests
– <strong>Time saved annually:</strong> 100 minutes
– <strong>Payback period:</strong> 12 builds (~6 months)</p>

<p>Plus, we eliminated a security risk. The real value isn&#39;t just time – it&#39;s confidence and safety.</p>

<h3 id="2-separate-upstream-from-custom">2. Separate Upstream from Custom</h3>

<p>We keep the upstream Helm chart (<code>Chart.yaml</code>) completely untouched. Our customizations live in:
– Custom Dockerfiles (add Sentry)
– Values overrides (<code>values-river.yaml</code>)
– Build scripts</p>

<p><strong>Why this matters:</strong> We can pull upstream chart updates without conflicts. Our changes are additive, not modifications.</p>

<h3 id="3-test-incrementally">3. Test Incrementally</h3>

<p>We didn&#39;t just run the full build and hope it worked. We tested:</p>
<ol><li>✓ Credential retrieval from Infisical</li>
<li>✓ JSON parsing with <code>jq</code></li>
<li>✓ Docker login with retrieved credentials</li>
<li>✓ Image builds</li>
<li>✓ Image pushes to registry</li>
<li>✓ Kubernetes deployment</li>
<li>✓ Running version verification</li></ol>

<p>Each step validated before moving forward. When something broke (initial credential permissions), we caught it immediately.</p>

<h3 id="4-documentation-is-for-future-you">4. Documentation Is for Future You</h3>

<p>We wrote a comprehensive retrospective covering:
– What went well
– What we learned
– What we&#39;d do differently next time
– Troubleshooting guides for common issues</p>

<p>In 6 months when we upgrade to v4.6.0, we&#39;ll thank ourselves for this documentation.</p>

<h3 id="5-version-numbers-tell-a-story">5. Version Numbers Tell a Story</h3>

<p>Understanding semantic versioning helps assess risk:</p>
<ul><li><strong>v4.5.0 → v4.5.1</strong> = Patch release (bug fixes only, low risk)</li>
<li><strong>v4.5.x → v4.6.0</strong> = Minor release (new features, moderate risk)</li>
<li><strong>v4.x.x → v5.0.0</strong> = Major release (breaking changes, high risk)</li></ul>

<p>This informed our decision to proceed quickly with minimal testing.</p>

<hr>

<h2 id="what-we-d-do-differently-next-time">What We&#39;d Do Differently Next Time</h2>

<p>Despite the success, we identified improvements:</p>

<h3 id="high-priority">High Priority</h3>

<p><strong>1. Validate credentials before building</strong></p>

<p>Currently, we discover authentication failures during the image push (after building). Better:</p>

<pre><code class="language-bash"># Test login BEFORE building
if ! docker login ...; then
  echo &#34;❌ Auth failed&#34;
  exit 1
fi
</code></pre>

<p><strong>2. Initialize Infisical project config</strong></p>

<p>Running <code>infisical init</code> in the project directory creates a <code>.infisical.json</code> file, eliminating the need for <code>--projectId</code> flags in every command.</p>

<p><strong>3. Add version consistency checks</strong></p>

<p>A simple script to verify all 4 files have matching versions before building would catch human errors.</p>

<h3 id="medium-priority">Medium Priority</h3>

<p><strong>4. Automated deployment verification</strong></p>

<p>Replace manual <code>kubectl</code> checks with a script that:
– Waits for pods to be ready
– Extracts running version
– Compares to expected version
– Reports success/failure</p>

<p><strong>5. Dry-run mode for build script</strong></p>

<p>Test the script logic without actually building or pushing images. Useful for testing changes to the script itself.</p>

<hr>

<h2 id="the-impact">The Impact</h2>

<p><strong>Before this session:</strong>
– Manual credential management
– 5+ minutes per build for login
– Credentials in shell history (security risk)
– No audit trail</p>

<p><strong>After this session:</strong>
– Automated credential retrieval
– 0 minutes per build for login
– Credentials never exposed (security improvement)
– Full audit trail in Infisical
– Repeatable process documented</p>

<p><strong>Plus:</strong> We&#39;re running Mastodon v4.5.1 with 10 bug fixes, making our instance more stable for our users.</p>

<hr>

<h2 id="lessons-for-other-mastodon-admins">Lessons for Other Mastodon Admins</h2>

<p>If you run a Mastodon instance, here&#39;s what we learned that might help you:</p>

<h3 id="for-small-instances">For Small Instances</h3>

<p>Even if you&#39;re running standard Mastodon without customizations:</p>
<ol><li><strong>Document your upgrade process</strong> – Your future self will thank you</li>
<li><strong>Test in staging first</strong> – If you don&#39;t have staging, test with dry-run/simulation</li>
<li><strong>Always check release notes</strong> – 5 minutes of reading prevents hours of debugging</li>
<li><strong>Use semantic versioning to assess risk</strong> – Patch releases are usually safe</li></ol>

<h3 id="for-custom-deployments">For Custom Deployments</h3>

<p>If you run custom images like we do:</p>
<ol><li><strong>Separate upstream from custom</strong> – Keep modifications isolated and additive</li>
<li><strong>Automate credential management</strong> – Shell history is not secure storage</li>
<li><strong>Use Docker layer caching</strong> – Speeds up builds dramatically</li>
<li><strong>Platform flags matter</strong> – <code>--platform linux/amd64</code> if deploying to different architecture</li>
<li><strong>Verify the running version</strong> – Don&#39;t assume deployment worked, check it</li></ol>

<h3 id="for-kubernetes-deployments">For Kubernetes Deployments</h3>

<p>If you deploy to Kubernetes:</p>
<ol><li><strong>Rolling updates are your friend</strong> – Zero downtime is achievable</li>
<li><strong>Helm revisions enable easy rollback</strong> – <code>helm rollback</code> is simple and fast</li>
<li><strong>Verify pod image versions</strong> – Check what&#39;s actually running, not just deployed</li>
<li><strong>Monitor during rollout</strong> – Watch pod status, don&#39;t just fire and forget</li></ol>

<hr>

<h2 id="the-numbers">The Numbers</h2>

<p><strong>Session Duration:</strong> 90 minutes total
– Research: 15 minutes
– Version updates: 10 minutes
– Infisical integration: 60 minutes
– Build &amp; deploy: 5 minutes</p>

<p><strong>Deployment Stats:</strong>
– <strong>Downtime:</strong> 0 seconds (rolling update)
– <strong>Pods affected:</strong> 3 (web, streaming, sidekiq)
– <strong>Helm revision:</strong> 166
– <strong>Rollback complexity:</strong> Low (single command)</p>

<p><strong>Lines of code changed:</strong> 18 lines across 4 files
<strong>Lines of documentation written:</strong> 629 lines (retrospective)
<strong>Security improvements:</strong> 1 major (credential management)</p>

<hr>

<h2 id="final-thoughts">Final Thoughts</h2>

<p>What started as a simple patch upgrade turned into a significant infrastructure improvement. The version bump was almost trivial – the real work was automating away manual steps and eliminating security risks.</p>

<p>This is what good ops work looks like: using routine maintenance as an opportunity to make systems better. The 60 minutes we spent on Infisical integration will pay dividends on every future build. The documentation we wrote will help the next person (or future us) upgrade with confidence.</p>

<p>Mastodon v4.5.1 is running smoothly, our build process is more secure, and we learned lessons that will make the next upgrade even smoother.</p>

<hr>

<h2 id="resources">Resources</h2>

<p><strong>For Mastodon Admins:</strong>
– <a href="https://docs.joinmastodon.org/admin/upgrading/" rel="nofollow">Mastodon Upgrade Documentation</a>
– <a href="https://github.com/glitch-soc/mastodon/releases" rel="nofollow">glitch-soc Releases</a></p>

<p><strong>For Infrastructure:</strong>
– <a href="https://infisical.com/" rel="nofollow">Infisical (Secrets Management)</a>
– <a href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/" rel="nofollow">Docker Build Best Practices</a>
– <a href="https://helm.sh/docs/helm/helm_upgrade/" rel="nofollow">Helm Upgrade Documentation</a></p>

<p><strong>Our Instance:</strong>
– <a href="https://river.group.lt/" rel="nofollow">river.group.lt</a> – Live Mastodon instance
– Running glitch-soc v4.5.1+glitch
– Kubernetes + Helm deployment
– Custom images with Sentry monitoring</p>

<hr>

<h2 id="questions">Questions?</h2>

<p>If you&#39;re running a Mastodon instance and have questions about:
– Upgrading glitch-soc variants
– Custom Docker image workflows
– Kubernetes deployments
– Secrets management with Infisical
– Zero-downtime upgrades</p>

<p>Feel free to reach out! We&#39;re happy to share what we&#39;ve learned.</p>

<hr>

<p><strong>Tags:</strong> <a href="/saint/tag:mastodon" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">mastodon</span></a> <a href="/saint/tag:glitch" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">glitch</span></a>-soc <a href="/saint/tag:kubernetes" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">kubernetes</span></a> <a href="/saint/tag:devops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devops</span></a> <a href="/saint/tag:infrastructure" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">infrastructure</span></a> <a href="/saint/tag:security" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">security</span></a> <a href="/saint/tag:automation" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">automation</span></a></p>

<hr>

<p><em>This blog post is part of our infrastructure documentation series. We believe in sharing knowledge to help others running similar systems. All technical details are from our actual upgrade session on November 13, 2025.</em></p>

<p>Comment in the Fediverse <a href="https://avys.group.lt/@/saint@river.group.lt" class="u-url mention" rel="nofollow">@<span>saint@river.group.lt</span></a></p>
]]></content:encoded>
      <guid>https://avys.group.lt/saint/upgrading-mastodon-to-v4-5-1-a-journey-in-automation-and-security</guid>
      <pubDate>Thu, 13 Nov 2025 22:31:41 +0000</pubDate>
    </item>
  </channel>
</rss>