<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Alek&#x27;s Blog - caddy</title>
      <link>https://blog.none.at</link>
      <description>Production notes on Kubernetes, OpenShift, and OVHcloud: observability, log archiving, service mesh, LLM inference, and digital sovereignty.</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://blog.none.at/tags/caddy/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Wed, 04 Mar 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Building a native file upload handler for Caddy v2</title>
          <pubDate>Tue, 01 Mar 2022 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2022/2022-03-01-caddy-upload/</link>
          <guid>https://blog.none.at/blog/2022/2022-03-01-caddy-upload/</guid>
          <description xml:base="https://blog.none.at/blog/2022/2022-03-01-caddy-upload/">&lt;p&gt;When &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;caddyserver&#x2F;caddy&#x2F;releases&#x2F;tag&#x2F;v2.0.0&quot;&gt;Caddy v2&lt;&#x2F;a&gt; was released in 2020, it introduced a new concept of handlers that use Go’s native module features.&lt;&#x2F;p&gt;
&lt;p&gt;In 2022 one of our customers needed to upload files, and at that time none of the available web servers could handle this natively without an extra program. So I decided to create a new handler for Caddy v2 — &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;caddyv2-upload&quot;&gt;caddyv2-upload&lt;&#x2F;a&gt; was born.&lt;&#x2F;p&gt;
&lt;p&gt;I was not aware of any other tool which can handle file uploads natively in just one binary which can be used in Kubernetes or OpenShift, or I have not searched hard enough 😁. I also wanted to learn go, so I decided to create a new handler for the go based webserver &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;caddyserver.com&#x2F;&quot;&gt;caddyserver&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In May 2022 &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sigoden&#x2F;dufs&quot;&gt;dufs&lt;&#x2F;a&gt;, a Rust-based server, was created — so that might be another option worth considering. 🤷&lt;&#x2F;p&gt;
&lt;p&gt;Any how I wanted to learn go in the pre AI Area and at that time I looked into the docs of go modules, into the source of caddy servers own handlers and created step by step a new handler.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily there was already podman and buildah, so I was able to create container images to run and test locally.&lt;&#x2F;p&gt;
&lt;p&gt;Building something real turned out to be the most effective way to learn Go. Diving into Caddy’s own handler source code, understanding the module system, and iterating with containers made the concepts stick far better than any tutorial would have.&lt;&#x2F;p&gt;
&lt;p&gt;A minimal Caddyfile configuration looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    order upload before file_server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;:2015 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    root .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    file_server browse&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    @mypost method POST&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    upload @mypost {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        dest_dir &#x2F;var&#x2F;uploads&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        max_filesize 4MB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Build with &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;caddyserver&#x2F;xcaddy&quot;&gt;xcaddy&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;xcaddy&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-with&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; github.com&#x2F;git001&#x2F;caddyv2-upload&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Today &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;caddyv2-upload&quot;&gt;caddyv2-upload&lt;&#x2F;a&gt; (v0.20.0) supports file uploads via HTTP multipart form, configurable destination directories, UUID-based subdirectory creation per upload, fixed filenames, pass-through mode to continue to the next Caddy handler after upload, configurable file size limits, an HTTP notification callback after successful uploads, and a &lt;code&gt;&#x2F;health&lt;&#x2F;code&gt; endpoint. It integrates with Caddy’s variable system and works in Kubernetes and OpenShift environments. Authentication is handled by Caddy’s own security layer rather than the module itself.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
