<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="/atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://duggan.ie/tag/docker/</id>
  <title type="text">@duggan — docker</title>
  <updated>2026-01-05T20:59:48.000Z</updated>
  <author>
    <name>Ross Duggan</name>
    <email>ross@duggan.ie</email>
    <uri>https://duggan.ie/</uri>
  </author>
  <icon>https://duggan.ie/favicon.ico</icon>
  <link href="https://duggan.ie/tag/docker/atom.xml" rel="first"/>
  <link href="https://duggan.ie/tag/docker/atom.xml?page=1" rel="last"/>
  <link href="https://duggan.ie/tag/docker/atom.xml" rel="self"/>
  <logo>https://duggan.ie/og-image.png</logo>
  <rights type="text">All rights reserved 2026, Ross Duggan</rights>
  <subtitle type="text">Posts tagged docker</subtitle>
  <entry>
    <id>https://duggan.ie/posts/self-hosting-git-and-builds-without-running-a-bunch-of-web-services</id>
    <title type="text">Self-hosting git and builds without running a bunch of web services</title>
    <updated>2026-01-05T20:59:48.000Z</updated>
    <author>
      <name>Ross Duggan</name>
      <email>ross@duggan.ie</email>
      <uri>https://duggan.ie/</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;My main side project at the moment is this blog. I tinker away with it when I feel like it. I'm not working on it with anyone else, not fielding pull requests, and not writing documentation.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;That said, I do still like having it in version control. I like having a &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;Dockerfile&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; to encapsulate dependencies and configuration, and using docker compose as a portable runtime manager. It's probably not everyone's idea of a simple setup, but I like it.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;The blog is deployed to a small VPS in &lt;/span&gt;&lt;a href="http://hetzner.com" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Hetzner&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, running behind nginx.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;GitHub, Github Actions, and the GitHub Container Registry have been how I've been handling builds, but it's been slow, and it's started to seem absurd to have all these builds occurring in some random datacenter presumably on the other side of the Atlantic ocean, that I am then pulling back to a server in Europe.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I started fishing around for a locally hosted replacement, coming across &lt;/span&gt;&lt;a href="http://forgejo.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Forgejo&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, &lt;/span&gt;&lt;a href="http://woodpecker-ci.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Woodpecker CI&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, and &lt;/span&gt;&lt;a href="https://onedev.io" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;OneDev&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; in addition to more familiar names like &lt;/span&gt;&lt;a href="https://gitlab.com" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;GitLab&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; and &lt;/span&gt;&lt;a href="http://sourcehut.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;SourceHut&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;At some point it occurred to me that this was a lot of ceremony for something relatively simple. How hard could it be just to have a git remote and hang some builds off it? I'm not trying to launch my own GitHub here.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I want to:&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li value="1"&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;git push&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; to a &lt;/span&gt;&lt;span class="tooltip-wrapper tooltip-trigger cursor-help underline decoration-dotted" data-tooltip="true"&gt;&lt;span style="white-space: pre-wrap;"&gt;central&lt;/span&gt;&lt;template data-tooltip-content=""&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I code from a couple of different machines, so having an actual central remote upstream makes things easier.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;You could probably do a lot of this on just your local machine with a few tweaks.&lt;/span&gt;&lt;/p&gt;&lt;/template&gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; repo&lt;/span&gt;&lt;/li&gt;&lt;li value="2"&gt;&lt;span style="white-space: pre-wrap;"&gt;When I push, it kicks of a container image build&lt;/span&gt;&lt;/li&gt;&lt;li value="3"&gt;&lt;span style="white-space: pre-wrap;"&gt;That image is pushed to an image registry that I can deploy from&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;This does not need several UIs and databases. In any case git being git, you can always switch out for something more complex later.&lt;/span&gt;&lt;/p&gt;&lt;aside class="callout my-6"&gt;&lt;div class="border-l-4 border-blue-500 bg-blue-50 dark:bg-blue-900/20 px-6 py-4 rounded-r-lg"&gt;&lt;strong class="block text-sm font-semibold uppercase tracking-wide text-blue-700 dark:text-blue-300"&gt;Note&lt;/strong&gt;&lt;div class="mt-2 text-base leading-relaxed"&gt;&lt;p class="mb-0"&gt;One piece of plumbing that makes all this much more flexible is Tailscale. It is not strictly required, but having the various machines on the same private network, regardless of their physical location, makes some parts easier.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;&lt;h3 id="hosting-a-git-repo" class="group relative"&gt;&lt;a href="#hosting-a-git-repo" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Hosting a git repo&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;On GitHub, an SSH clone URL looks something like:&lt;/span&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;git@github.com:duggan/duggan.ie.git&lt;/span&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;It's an SSH URI just like it would be with &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;scp&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; or &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;rsync&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;. A helpful breakdown from &lt;/span&gt;&lt;a href="https://stackoverflow.com/a/70330178" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Mike Slinn and Jaditpol on StackOverflow&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4"&gt;&lt;span style="white-space: pre-wrap;"&gt;    git@github&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;com&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;myuser&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;myrepo&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    \_&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; \________&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; \_______________&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;     &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;|&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;      &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;|&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;              &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;|&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;    user   host           path&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Since I'm the only user, I don't need to get fancy, and I can just use my own login.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;The &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;origin&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; of a git repo is more or less just the contents of the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;.git&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; directory in a remote location. That's it. You don't even need to run a git server if you're happy enough using ssh for transport.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I put it outside my home path just to keep it a little out of the way, as I won't need to interact with it too often:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6"&gt;&lt;span style="white-space: pre-wrap;"&gt;sudo mkdir &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;p &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;srv&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;sudo chown &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;R&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;USER&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;$&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;USER&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;srv&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;cd &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;srv&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;mkdir example&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;cd example&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;git init &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;bare&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;In my case I've replaced my GitHub &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;origin&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; with this one directly in the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;.git/config&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; of my local checkout, but when I was testing the waters I just added an additional remote:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1"&gt;&lt;span style="white-space: pre-wrap;"&gt;$ git remote add test ross@buildmachine&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;srv&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;duggan&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;ie&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;/pre&gt;&lt;h3 id="building-an-image-on-push" class="group relative"&gt;&lt;a href="#building-an-image-on-push" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Building an image on push&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Git's own hooks system, combined with a Makefile and Docker, is enough to put together a pretty flexible build system for anything you can deploy using a container.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;In my newly minted upstream repo, I added a file named &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;post-receive&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; into the &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;hooks&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; directory, i.e., &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;/srv/git/duggan.ie.git/hooks/post-receive&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; (and &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;chmod +x&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; it):&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38"&gt;&lt;span style="white-space: pre-wrap;"&gt;#!/bin/bash&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;set &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;e&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;# Read the push &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;info&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;branch being updated&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;while&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; read oldrev newrev refname&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;do&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; # Only trigger on main branch&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;[&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;[&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; $refname &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;==&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"refs/heads/main"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;]&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;]&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; then&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"=== Build triggered for main branch ==="&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # Get repo name from current directory&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REPO_NAME&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;basename &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$(pwd)"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;WORK_TREE&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"/tmp/git-build-${REPO_NAME}"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;GIT_DIR&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;pwd&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # Clean checkout&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; rm &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;rf &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$WORK_TREE"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mkdir &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;p &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$WORK_TREE"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; git &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;work&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;tree&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$WORK_TREE"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;--&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;git&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;dir&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$GIT_DIR"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; checkout &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;f main&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cd &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$WORK_TREE"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # Check &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;for&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Makefile and build target&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;[&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;[&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;f Makefile &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;]&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;]&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; then&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;if&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; grep &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;q &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"^build:"&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; Makefile&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; then&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"=== Running make build ==="&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; make build&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"=== Build complete ==="&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;else&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"Makefile found but no 'build' target, skipping"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fi&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;else&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"No Makefile found, skipping build"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fi&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # Cleanup&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; rm &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;rf &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"$WORK_TREE"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; fi&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;done&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;When I push to the repo, this looks for a &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;Makefile&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; in the root of the repo, checks for a &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;build&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; command and executes it.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;figure class="video-wrapper"&gt;&lt;video src="/files/4ab4a809cd6e7fa2.mp4" autoplay="" muted="" loop="" playsinline="" data-playback="silentLoop" title="push1.mp4" style="max-width: 100%; height: auto"&gt;&lt;/video&gt;&lt;/figure&gt;&lt;span style="white-space: pre-wrap;"&gt;My &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;Makefile&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; is super basic, just:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5"&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IMAGE&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;=&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;localhost&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;5000&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;duggan&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;ie&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;latest&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;build&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;docker build &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;t &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IMAGE&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;	&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;docker push &lt;/span&gt;&lt;span class="editor-tokenFunction" style="white-space: pre-wrap;"&gt;$&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;(&lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;IMAGE&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;As a bonus, I could have thrown that post-receive script into a &lt;/span&gt;&lt;span class="tooltip-wrapper tooltip-trigger cursor-help underline decoration-dotted" data-tooltip="true"&gt;&lt;span style="white-space: pre-wrap;"&gt;git template&lt;/span&gt;&lt;template data-tooltip-content=""&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;For example, you can throw `post-receive` into a directory at `/srv/git/template` then configure via the git config:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;git config --global init.templateDir /srv/git/template&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;/template&gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; so that new repos are automatically created with a copy of the post receive script, but it doesn't seem necessary just yet.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;On the build machine I've also enabled my user account to run docker directly without requiring &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;sudo&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;div class="link-preview-card border border-gray-200 rounded-lg overflow-hidden my-4"&gt;&lt;a href="https://docs.docker.com/engine/install/linux-postinstall/" target="_blank" rel="noopener noreferrer" class="no-underline text-inherit flex flex-row"&gt;&lt;div class="flex-1 p-3 min-w-0"&gt;&lt;div class="link-preview-site flex items-center gap-1.5 mb-2 text-xs text-gray-500"&gt;&lt;img src="/files/7d2f187075a49630.svg" class="w-4 h-4 m-0"&gt;&lt;span&gt;Docker Documentation&lt;/span&gt;&lt;/div&gt;&lt;div class="link-preview-title font-semibold text-base leading-tight mb-1"&gt;Post-installation steps&lt;/div&gt;&lt;div class="link-preview-description text-sm text-gray-500 leading-snug line-clamp-2"&gt;Find the recommended Docker Engine post-installation steps for Linux users, including how to run Docker as a non-root user and more.&lt;/div&gt;&lt;div class="text-xs text-gray-400 mt-2"&gt;docs.docker.com&lt;/div&gt;&lt;/div&gt;&lt;div class="w-[200px] shrink-0 overflow-hidden bg-gray-100"&gt;&lt;img src="/files/9fdaa65db57e044d.webp" alt="Post-installation steps" class="w-full h-full object-cover m-0"&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;&lt;h3 id="hosting-an-image" class="group relative"&gt;&lt;a href="#hosting-an-image" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Hosting an image&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;At this point the image is built, which will be fine if I just wanted to run the container on the same machine, but I need to deploy this to my little Hetzner VPS.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;This is where Tailscale comes in handy, as it lets me have Docker's own &lt;/span&gt;&lt;a href="https://distribution.github.io/distribution/" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;container registry&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; running on my build server, which I then pull from on the VPS. It's very straightforward to set up.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;On the build server I have a &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;docker-compose.yml&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; file with the registry configured:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4
5
6
7
8
9
10
11"&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;services&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; registry&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; image&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; registry&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; container_name&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; registry&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; restart&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; always&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; ports&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"5000:5000"&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; volumes&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;-&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;data&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span class="editor-tokenKeyword" style="white-space: pre-wrap;"&gt;var&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;lib&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;registry&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; environment&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenConstant" style="white-space: pre-wrap;"&gt;REGISTRY_STORAGE_DELETE_ENABLED&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"true"&lt;/span&gt;&lt;/pre&gt;&lt;h3 id="deploying-from-a-private-registry" class="group relative"&gt;&lt;a href="#deploying-from-a-private-registry" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Deploying from a private registry&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;With the registry and VPS both on the Tailscale network, I can allow regular HTTP traffic, which requires a small tweak to Docker on the VPS.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;In &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;/etc/docker/daemon.json&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3"&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;{&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"insecure-registries"&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;[&lt;/span&gt;&lt;span class="editor-tokenString" style="white-space: pre-wrap;"&gt;"buildmachine:5000"&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;With that done, I'm able to update my &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;make deploy&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt; command to point at the new registry:&lt;/span&gt;&lt;/p&gt;&lt;pre class="editor-code line-numbers" spellcheck="false" data-highlight-language="javascript" data-gutter="1
2
3
4"&gt;&lt;span style="white-space: pre-wrap;"&gt;services&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; web&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;&amp;nbsp; &amp;nbsp; image&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; buildmachine&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span class="editor-tokenNumber" style="white-space: pre-wrap;"&gt;5000&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;/&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;duggan&lt;/span&gt;&lt;span class="editor-tokenPunctuation" style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;ie&lt;/span&gt;&lt;span class="editor-tokenOperator" style="white-space: pre-wrap;"&gt;:&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;latest&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;This all works much more quickly than before, and since it's running the builds over SSH directly, I'm getting feedback faster than I would refreshing a build log.&lt;/span&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;There is currently no comments system. If you'd like to share an opinion either with me or about this post, please feel free to do so with me either via email (&lt;/em&gt;&lt;/i&gt;&lt;a href="mailto:ross@duggan.ie" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;ross@duggan.ie&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;) on Mastodon (&lt;/em&gt;&lt;/i&gt;&lt;a href="http://mastodon.ie/@duggan" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;@duggan@mastodon.ie&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;) or even on &lt;/em&gt;&lt;/i&gt;&lt;a href="https://news.ycombinator.com" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;Hacker News&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;a href="https://news.ycombinator.com" rel="noreferrer"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;.&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;</content>
    <link href="https://duggan.ie/posts/self-hosting-git-and-builds-without-running-a-bunch-of-web-services" rel="alternate"/>
    <published>2026-01-12T00:12:06.449Z</published>
    <summary type="html">&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;GitHub, Github Actions, and the GitHub Container Registry have been how I've been handling builds, but it's been slow, and it's started to seem absurd to have all these builds occurring in some random datacenter presumably on the other side of the Atlantic ocean, that I am then pulling back to a server in Europe.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I started fishing around for a locally hosted replacement, coming across &lt;/span&gt;&lt;a href="http://forgejo.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Forgejo&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, &lt;/span&gt;&lt;a href="http://woodpecker-ci.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Woodpecker CI&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, and &lt;/span&gt;&lt;a href="https://onedev.io" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;OneDev&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; in addition to more familiar names like &lt;/span&gt;&lt;a href="https://gitlab.com" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;GitLab&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; and &lt;/span&gt;&lt;a href="http://sourcehut.org" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;SourceHut&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;At some point it occurred to me that this was a lot of ceremony for something relatively simple. How hard could it be just to have a git remote and hang some builds off it? I'm not trying to launch my own GitHub here.&lt;/span&gt;&lt;/p&gt;</summary>
  </entry>
</feed>