aboutsummaryrefslogtreecommitdiff
path: root/public/bringing-all-of-my-projects-together-under-one-umbrella.html
blob: bc6deb89b75947a733a6bc5bb099c315648f22c0 (plain)
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
<!doctype html><html lang=en-us><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=generator content="JBMAFP - github.com/mitjafelicijan/jbmafp"><link href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL69vf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv76+/8LBwQkAAAAAAAAAAAAAAAC+vb3/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL+9vf/Bv78JAAAAAAAAAAAAAAAAu7q6/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC7ubr/vr29CAAAAAAAAAAAy8nJAZ6foP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnqGj/6GipAoAAAAAHLjU/xcXHf/BwsL/I8XY/yPK3v8XGiD/IbjL/yPF2f8XGiD/Fxkf/yLF2f8gnK3/Fxog/62ztv8fwNf/FRcd/x271v8mz93/GRsi/xkXHf8p097/GiIp/xobIv8p0t3/KdPe/xocIv8fYmr/KNPe/xoZH/8aHCL/J87c/xy81/8VFxz/IsPZ/8zS0/8XGiD/Ir/R/yPH2/8XGiD/Fxkf/yPH2/8dd4T/GBog/yPJ3f8jyNr/uru9/xcUGv8cudb/EhITDKi5vRKlvMP/RUpOERwcHRAdOj4QHTk8EBwdHRAdNTgQHTo/EBwcHRAcHB0QSGduEKW4vf+koqQfHzg+EBqz0ewSFRv7EyMr/xq51vsTERb7ExUb+xq41fsau9j7ExUb+xiPp/sZudb7ExUb+xMVG/sZuNX/GKvI/BIUGfMdvdn/IrfL/xcaIP8n1eb/J9Dh/xkcIf8ZGR7/J8/f/xxCSv8ZGyH/J9Dg/ybQ4P8ZHCL/FSQs/yPK3/8UExj/GE1b/ybS5P8ZGB7/Ghwj/ynW5P8p2Ob/Ghwi/yWrtv8p1eH/Ghwi/xocIv8p1uT/J8XT/xkcIv8m1un/Hb7d/xUYH/8hzOr/HtHu/xcaIf8XGB//I8vi/xgxOv8XGSD/I8rg/yPK4P8XGiD/GUFL/yPP6f8SERj/Fhkh/x3A4f8AAAAAJ2f9/ydr//8mZPH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlYu38J2v//ydo/f8AAAAAAAAAAAd8/fkFqf//Iob8sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMY39awWr//8FfP3/AAAAAAAAAAAFm/7/SfD//wR+/f8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOB/f9B7v//BaX+/wAAAAAAAAAAQ878SAyZ/v9n1v4KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADu9v8DDJb+/z3N/XgAAAAA3/sAAN/7AADf+wAA3/sAAAAAAAAAAAAAAAAAAN/7AAAAAAAAAAAAAAAAAAAAAAAAj/EAAI/5AACP8QAA3/sAAA==" rel=icon type=image/x-icon><title>Bringing all of my projects together under one umbrella</title><meta name=description content="What is the issue anyway?"><link rel=alternate type=application/rss+xml title="Mitja Felicijan's posts" href=https://mitjafelicijan.com/index.xml><link rel=alternate type=application/rss+xml title="Mitja Felicijan's notes" href=https://mitjafelicijan.com/notes.xml><style>body{padding:2.5rem;max-width:1900px;background:#fff;font-family:sans-serif;line-height:1.35rem;font-size:16px}hr{margin-block-start:1.5rem}h1,h2,h3{line-height:initial}h1{font-size:xx-large}footer{margin-block-start:2rem}cap{text-transform:capitalize}table{max-width:100%;border-collapse:separate;border-spacing:2px;border:1px solid #000;border-left:1px solid #999;border-top:1px solid #999}blockquote{font-style:italic}table thead{background:#eee}ul.list li{padding:.2em 0}ul{line-height:1.4em}td,th{border:1px solid #000;padding:4px;border-right:1px solid #999;border-bottom:1px solid #999;text-align:left}pre{text-wrap:nowrap;overflow-x:auto;padding:0 1em;border:1px solid #dcdcdc}code{padding:0 3px;font-size:14px;border:0}pre code{line-height:1.3em}pre,code,pre *,code *{font-family:monospace}figure{margin-inline-start:0;margin-inline-end:0}figcaption{text-align:center}figcaption p{margin:.3em 0 0}img,video,audio{width:800px;max-width:100%}header{display:flex;flex-direction:row;gap:6rem}nav{display:flex;gap:.75rem}nav.main{}.pstatus-orange{background:gold}.pstatus-green{background:#9acd32}.pstatus-red{background:#cd5c5c}@media only screen and (max-width:600px){body{padding:15px}header{flex-direction:column;gap:1rem}a{word-wrap:break-word}}</style><header><nav class=main itemscope itemtype=http://schema.org/SiteNavigationElement role=toolbar><a href=/>Home</a>
<a href=https://github.com/mitjafelicijan target=_blank>Code</a>
<a href=/vault.html>Vault</a>
<a href=/mitjafelicijan.pgp.pub.txt target=_blank>PGP</a>
<a href=/curriculum-vitae.html>CV</a>
<a href=/index.xml target=_blank>RSS</a></nav></header><main role=main><article itemtype=http://schema.org/Article><h1 itemtype=headline>Bringing all of my projects together under one umbrella</h1><p><cap>post</cap>, Jul 1, 2023 on <a href=https://mitjafelicijan.com>Mitja Felicijan's blog</a><div><h2 id=what-is-the-issue-anyway>What is the issue anyway?</h2><p>Over the years, I have accumulated a bunch of virtual servers on my
<a href=https://www.digitalocean.com/>DigitalOcean</a> account for small experimental
projects I dabble in. And this has resulted in quite a bill. I mean, I wouldn't
care if these projects were actually being used. But there were just being there
unused and wasting resources. Which makes this an unnecessary burden for me.<p>Most of them are just small HTML pages that have an endpoint or two to read data
from or to, and for that reason I wrote servers left and right. To be honest,
all of those things could have been done with <a href=https://en.wikipedia.org/wiki/Common_Gateway_Interface>CGI
scripts</a> and that would
have been more than enough.<p>Recently, I decided to stop language hopping and focus on a simpler stack which
includes C, Go and Lua. And I can accomplish all the things I am interested in.<h2 id=finding-a-web-server-replacement>Finding a web server replacement</h2><p>Usually I had <a href=https://nginx.org/en/>Nginx</a> in front of these small web servers
and I had to manage SSL certificates and all that jazz. I am bored with these
things. I don't want to manage any of this bullshit anymore.<p>So the logical move forward was to find a solid alternative for this. I have
ended up on <a href=https://caddyserver.com/>Caddy server</a>. I've used it in the past
but kind of forgotten about it. What I really like about it is an ease of use
and a bunch of out of the box functionalities that come with it.<p>These are the <em>pitch</em> points from their website:<ul><li><strong>Secure by Default</strong>: Caddy is the only web server that uses HTTPS by
default. A hardened TLS stack with modern protocols preserves privacy and
exposes MITM attacks.<li><strong>Config API</strong>: As its primary mode of configuration, Caddy's REST API makes
it easy to automate and integrate with your apps.<li><strong>No Dependencies</strong>: Because Caddy is written in Go, its binaries are entirely
self-contained and run on every platform, including containers without libc.<li><strong>Modular Stack</strong>: Take back control over your compute edge. Caddy can be
extended with everything you need using plugins.</ul><p>I had just a few requirements:<ul><li>Automatic SSL<li>Static file server<li>Basic authentication<li>CGI script support</ul><p>And the vanilla version does all of it, but CGI scripts. But that can easily be
fixed with their modular approach. You can do this on their website and build a
custom version of the server, or do it with Docker.<p>This is a <code>Dockerfile</code> I used to build a custom server.<pre tabindex=0 style=background-color:#fff><code><span style=display:flex><span><span style=color:#00f>FROM</span><span style=color:#a31515> caddy:builder AS builder</span><span>
</span></span></span><span style=display:flex><span><span>
</span></span></span><span style=display:flex><span><span></span><span style=color:#00f>RUN</span> xcaddy build <span style=color:#a31515>\
</span></span></span><span style=display:flex><span><span style=color:#a31515></span>  --with github.com/aksdb/caddy-cgi<span>
</span></span></span><span style=display:flex><span><span>
</span></span></span><span style=display:flex><span><span></span><span style=color:#00f>FROM</span><span style=color:#a31515> caddy:latest</span><span>
</span></span></span><span style=display:flex><span><span></span><span style=color:#00f>RUN</span> apk add --no-cache nano<span>
</span></span></span><span style=display:flex><span><span>
</span></span></span><span style=display:flex><span><span></span><span style=color:#00f>COPY</span> --from=builder /usr/bin/caddy /usr/bin/caddy<span>
</span></span></span></code></pre><h2 id=getting-rid-of-all-the-unnecessary-virtual-machines>Getting rid of all the unnecessary virtual machines</h2><p>The next step was to get a handle on the number of virtual servers I have all
over the place.<p>I decided to move all the projects and services into two main VMs:<ul><li>personal server (still Nginx)<ul><li>git server<li>static file server<li>personal blog</ul><li>projects server (Caddy server)<ul><li>personal experiments<li>other projects</ul></ul><p>I will focus on projects' server in this post since it's more interesting.<h2 id=testing-cgi-scripts>Testing CGI scripts</h2><p>The first thing I tested was how CGI scripts work under Caddy. This is
particularly import to me because almost all of my experiments and mini projects
need this to work.<p>To configure Caddy server, you must provide the server with a configuration
file. By default, it's called <code>Caaddyfile</code>.<pre tabindex=0 style=background-color:#fff><code><span style=display:flex><span>{
</span></span><span style=display:flex><span>  <span style=color:#00f>order</span> <span style=color:#a31515>cgi</span> before <span style=color:#a31515>respond</span>
</span></span><span style=display:flex><span>}
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=font-weight:700>examples.mitjafelicijan.com</span> {
</span></span><span style=display:flex><span>  <span style=color:#00f>cgi</span> /bash-test <span style=color:#a31515>/opt/projects/examples/bash-test.sh</span>
</span></span><span style=display:flex><span>  <span style=color:#00f>cgi</span> /tcl-test <span style=color:#a31515>/opt/projects/examples/tcl-test.tcl</span>
</span></span><span style=display:flex><span>  <span style=color:#00f>cgi</span> /lua-test <span style=color:#a31515>/opt/projects/examples/lua-test.lua</span>
</span></span><span style=display:flex><span>  <span style=color:#00f>cgi</span> /python-test <span style=color:#a31515>/opt/projects/examples/python-test.py</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>  <span style=color:#00f>root</span> * <span style=color:#a31515>/opt/projects/examples</span>
</span></span><span style=display:flex><span>  <span style=color:#00f>file_server</span>
</span></span><span style=display:flex><span>}
</span></span></code></pre><ul><li>The order is very important. Make sure that <code>order cgi before respond</code> is at
the top of the configuration file.<li>Also, when you run with Caddy v2, make sure you provide <code>adapter</code> argument
like this <code>/usr/bin/caddy run --watch --environ --config /etc/caddy/Caddyfile --adapter caddyfile</code>. Otherwise, Caddy will try to use a different format for
config file.</ul><p>I did a small batch of tests with <a href=https://www.gnu.org/software/bash/>Bash</a>,
<a href=https://www.tcl-lang.org/>Tcl</a>, <a href=https://www.lua.org/>Lua</a> and
<a href=https://www.python.org/>Python</a>. Here is a cheat sheet if you need it.<p>Let's get Bash out of the way first.<pre tabindex=0 style=background-color:#fff><code><span style=display:flex><span><span style=color:#00f>#!/usr/bin/bash
</span></span></span><span style=display:flex><span><span style=color:#00f></span>
</span></span><span style=display:flex><span>printf <span style=color:#a31515>&#34;Content-type: text/plain\n\n&#34;</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>printf <span style=color:#a31515>&#34;Hello from Bash\n\n&#34;</span>
</span></span><span style=display:flex><span>printf <span style=color:#a31515>&#34;PATH_INFO     [%s]\n&#34;</span> $PATH_INFO
</span></span><span style=display:flex><span>printf <span style=color:#a31515>&#34;QUERY_STRING  [%s]\n&#34;</span> $QUERY_STRING
</span></span><span style=display:flex><span>printf <span style=color:#a31515>&#34;\n&#34;</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#00f>for</span> i in {0..9..1}; <span style=color:#00f>do</span>
</span></span><span style=display:flex><span>  printf <span style=color:#a31515>&#34;&gt; %s\n&#34;</span> $i
</span></span><span style=display:flex><span><span style=color:#00f>done</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>exit 0
</span></span></code></pre><p>This one is for Tcl script.<pre tabindex=0 style=background-color:#fff><code><span style=display:flex><span><span style=color:green>#!/usr/bin/tclsh
</span></span></span><span style=display:flex><span><span style=color:green>
</span></span></span><span style=display:flex><span><span style=color:green></span>puts <span style=color:#a31515>&#34;Content-type: text/plain\n&#34;</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>puts <span style=color:#a31515>&#34;Hello from Tcl\n&#34;</span>
</span></span><span style=display:flex><span>puts <span style=color:#a31515>&#34;PATH_INFO     \[$env(PATH_INFO)\]&#34;</span>
</span></span><span style=display:flex><span>puts <span style=color:#a31515>&#34;QUERY_STRING  \[$env(QUERY_STRING)\]&#34;</span>
</span></span><span style=display:flex><span>puts <span style=color:#a31515>&#34;&#34;</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#00f>for</span> <span style=color:#00f>{set</span> i 0<span style=color:#00f>}</span> <span style=color:#00f>{</span>$i &lt; 10<span style=color:#00f>}</span> <span style=color:#00f>{</span>incr i<span style=color:#00f>}</span> <span style=color:#00f>{</span>
</span></span><span style=display:flex><span>  puts <span style=color:#a31515>&#34;&gt; $i&#34;</span>
</span></span><span style=display:flex><span><span style=color:#00f>}</span>
</span></span></code></pre><p>And for all you Python enjoyers.<pre tabindex=0 style=background-color:#fff><code><span style=display:flex><span><span style=color:green>#!/usr/bin/python3</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#00f>import</span> os
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>print(<span style=color:#a31515>&#34;Content-type: text/plain</span><span style=color:#a31515>\n</span><span style=color:#a31515>&#34;</span>)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>print(<span style=color:#a31515>&#34;Hello from Python</span><span style=color:#a31515>\n</span><span style=color:#a31515>&#34;</span>)
</span></span><span style=display:flex><span>print(<span style=color:#a31515>&#34;PATH_INFO     [</span><span style=color:#a31515>{}</span><span style=color:#a31515>]&#34;</span>.format(os.environ[<span style=color:#a31515>&#39;PATH_INFO&#39;</span>]))
</span></span><span style=display:flex><span>print(<span style=color:#a31515>&#34;QUERY_STRING  [</span><span style=color:#a31515>{}</span><span style=color:#a31515>]&#34;</span>.format(os.environ[<span style=color:#a31515>&#39;QUERY_STRING&#39;</span>]))
</span></span><span style=display:flex><span>print(<span style=color:#a31515>&#34;&#34;</span>)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#00f>for</span> i <span style=color:#00f>in</span> range(10):
</span></span><span style=display:flex><span>  print(<span style=color:#a31515>&#34;&gt; </span><span style=color:#a31515>{}</span><span style=color:#a31515>&#34;</span>.format(i))
</span></span></code></pre><p>And for the final example, Lua.<pre tabindex=0 style=background-color:#fff><code><span style=display:flex><span><span style=color:#00f>#!/usr/bin/lua</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>print(<span style=color:#a31515>&#34;Content-type: text/plain</span><span style=color:#a31515>\n</span><span style=color:#a31515>&#34;</span>)
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>print(<span style=color:#a31515>&#34;Hello from Lua</span><span style=color:#a31515>\n</span><span style=color:#a31515>&#34;</span>)
</span></span><span style=display:flex><span>print(string.format(<span style=color:#a31515>&#34;PATH_INFO     [%s]&#34;</span>, os.getenv(<span style=color:#a31515>&#34;PATH_INFO&#34;</span>)))
</span></span><span style=display:flex><span>print(string.format(<span style=color:#a31515>&#34;QUERY_STRING  [%s]&#34;</span>, os.getenv(<span style=color:#a31515>&#34;QUERY_STRING&#34;</span>)))
</span></span><span style=display:flex><span>print()
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#00f>for</span> i = 0, 9 <span style=color:#00f>do</span>
</span></span><span style=display:flex><span>  print(string.format(<span style=color:#a31515>&#34;&gt; %d&#34;</span>, i))
</span></span><span style=display:flex><span><span style=color:#00f>end</span>
</span></span></code></pre><h2 id=basic-authentication>Basic authentication</h2><p>One thing was also to have an option for some sort of authentication, and
something like <a href=https://en.wikipedia.org/wiki/Basic_access_authentication>Basic access
authentication</a> would
be more than enough.<p>Thankfully, Caddy supports this out of the box already. Below is an updated
example.<pre tabindex=0 style=background-color:#fff><code><span style=display:flex><span>{
</span></span><span style=display:flex><span>    <span style=color:#00f>order</span> <span style=color:#a31515>cgi</span> before <span style=color:#a31515>respond</span>
</span></span><span style=display:flex><span>}
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=font-weight:700>examples.mitjafelicijan.com</span> {
</span></span><span style=display:flex><span>  <span style=color:#00f>cgi</span> /bash-test <span style=color:#a31515>/opt/projects/examples/bash-test.sh</span>
</span></span><span style=display:flex><span>  <span style=color:#00f>cgi</span> /tcl-test <span style=color:#a31515>/opt/projects/examples/tcl-test.tcl</span>
</span></span><span style=display:flex><span>  <span style=color:#00f>cgi</span> /lua-test <span style=color:#a31515>/opt/projects/examples/lua-test.lua</span>
</span></span><span style=display:flex><span>  <span style=color:#00f>cgi</span> /python-test <span style=color:#a31515>/opt/projects/examples/python-test.py</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>  <span style=color:#00f>root</span> * <span style=color:#a31515>/opt/projects/examples</span>
</span></span><span style=display:flex><span>  <span style=color:#00f>file_server</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>  <span style=color:#00f>basicauth</span> * {
</span></span><span style=display:flex><span>    <span style=color:#00f>bob</span> <span>$</span><span style=color:#a31515>2a</span><span>$</span>14<span>$</span><span style=color:#a31515>/wCgaf9oMnmQa20txB76u.nI1AldGMBT/1J7fXCfgOiRShwz/JOkK</span>
</span></span><span style=display:flex><span>  }
</span></span><span style=display:flex><span>}
</span></span></code></pre><p><code>basicauth *</code> matches everything under this domain/sub-domain and protects it
with Basic Authentication.<ul><li><code>bob</code> is the username<li><code>hash</code> is the password</ul><p>To generate these passwords, execute <code>caddy hash-password</code> and this will prompt
you to insert a password twice and spit out a hashed password that you can put
in your configuration file.<p>Restart the server and you are ready to go.<h2 id=making-caddy-a-service-with-systemd>Making Caddy a service with systemd</h2><p>After the tests were successful, I copied <code>caddy</code> to <code>/usr/bin/caddy</code> and copied
<code>Caddyfile</code> to <code>/etc/caddy/Caddyfile</code>.<p>Now off to the systemd. Each systemd service requires you to create a service
file.<ul><li>I created a <code>/etc/systemd/system/caddy.service</code> and put the following content
in the file.</ul><pre tabindex=0 style=background-color:#fff><code><span style=display:flex><span><span style=color:#00f>[Unit]</span>
</span></span><span style=display:flex><span>Description=<span style=color:#a31515>Caddy</span>
</span></span><span style=display:flex><span>Documentation=<span style=color:#a31515>https://caddyserver.com/docs/</span>
</span></span><span style=display:flex><span>After=<span style=color:#a31515>network.target network-online.target</span>
</span></span><span style=display:flex><span>Requires=<span style=color:#a31515>network-online.target</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#00f>[Service]</span>
</span></span><span style=display:flex><span>Type=<span style=color:#a31515>notify</span>
</span></span><span style=display:flex><span>User=<span style=color:#a31515>root</span>
</span></span><span style=display:flex><span>Group=<span style=color:#a31515>root</span>
</span></span><span style=display:flex><span>ExecStart=<span style=color:#a31515>/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile --adapter caddyfile</span>
</span></span><span style=display:flex><span>ExecReload=<span style=color:#a31515>/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force --adapter caddyfile</span>
</span></span><span style=display:flex><span>TimeoutStopSec=<span style=color:#a31515>5s</span>
</span></span><span style=display:flex><span>LimitNOFILE=<span style=color:#a31515>1048576</span>
</span></span><span style=display:flex><span>LimitNPROC=<span style=color:#a31515>512</span>
</span></span><span style=display:flex><span>PrivateTmp=<span style=color:#a31515>true</span>
</span></span><span style=display:flex><span>ProtectSystem=<span style=color:#a31515>full</span>
</span></span><span style=display:flex><span>AmbientCapabilities=<span style=color:#a31515>CAP_NET_ADMIN CAP_NET_BIND_SERVICE</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#00f>[Install]</span>
</span></span><span style=display:flex><span>WantedBy=<span style=color:#a31515>multi-user.target</span>
</span></span></code></pre><ul><li>You might need to reload systemd with <code>systemctl daemon-reload</code>.<li>Then I enabled the service with <code>systemctl enable caddy.service</code>.<li>And then I started the service with <code>systemctl start caddy.service</code>.</ul><p>This was about all that I needed to do to get it running. Now I can easily add
new subdomains and domains to the main configuration file and be done with
it. No manual Let's Encrypt shenanigans needed.</div></article></main><section><hr><h2>Posts from blogs I follow around the net</h2><ul><li><a href=https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSWhyNotDirectoryToFilesystem target=_blank rel=noopener>One reason that ZFS can't turn a directory into a filesystem</a><a href=https://utcc.utoronto.ca/~cks/space/blog/>Chris's Wiki :: blog</a><div>One of the wishes that I and other people frequently have for ZFS
is the ability to take an existing directory (and everything
underneath it) in a ZFS filesystem and turn it into a sub-filesystem
of its own. One reason for wanting this is that a number of things
are set and controlled on a per-filesyst…<li><a href=http://www.landley.net/notes-2023.html#28-10-2023 target=_blank rel=noopener>October 28, 2023</a><a href=http://www.landley.net/notes-2023.html>Rob Landley's Blog Thing for 2023</a><div>Oh good grief, two of my least favorite licensing people, Larry Rosen
and Bradley Kuhn, are interacting on the OSI's license-discuss
list where the're doing
bad computer history and insisting that a guy Larry Rosen
coincidentally interviewed for a book years ago is clearly the origin of
somethin…<li><a href="http://offbeatpursuit.com:80/blog/?id=25" target=_blank rel=noopener>A fix by any other name</a><a href=http://offbeatpursuit.com:80/blog/>WLOG - blog</a><div>tags:
i2c, plan9
Another month, another file system.
Well, if you can’t fix it in software, fix it in hardware (looking at
you, bme680, we’re not
done yet). The show must go on, as they say, and I would like my
experiments to go on.
So a “new” addition to the environmental sensor family connected to
the h…<li><a href=https://mirzapandzo.com/next-image-url-parameter-is-valid-but-upstream-response-is-invalid target=_blank rel=noopener>Next/Image "url" parameter is valid but upstream response is invalid</a><a href=https://mirzapandzo.com/>Mirza Pandzo's Blog</a><div>Getting "url" parameter is valid but upstream response is invalid error with Next/Image on WSL2<li><a href=https://drewdevault.com/2023/10/13/Going-off-script.html target=_blank rel=noopener>Going off-script</a><a href=https://drewdevault.com>Drew DeVault's blog</a><div>There is a phenomenon in society which I find quite bizarre. Upon our entry to
this mortal coil, we are endowed with self-awareness, agency, and free will.
Each of the 8 billion members of this human race represents a unique person, a
unique worldview, and a unique agency. Yet, many of us have the sam…<li><a href=https://szymonkaliski.com/writing/2023-10-02-building-a-diy-pen-plotter/ target=_blank rel=noopener>Building a DIY Pen Plotter</a><a href=http://github.com/dylang/node-rss>Szymon Kaliski</a><div>This article documents my learnings from designing and building a DIY Pen Plotter during the summer of 2023.
My ultimate goal is to build my…<li><a href=https://neil.computer/notes/chart-of-accounts-for-startups-and-saas-companies/ target=_blank rel=noopener>Chart of Accounts for Startups and SaaS Companies</a><a href=https://neil.computer/>Neil Panchal</a><div>Accounting is fundamental to starting a business. You need to have a basic understanding of accounting principles and essential bookkeeping. I had to learn it. There was no choice. For filing taxes, your CPA is going to ask you for an Income Statement (also known as P/L statement). If<li><a href=https://journal.valeriansaliou.name/deploy-a-nomad-cluster-on-alpine-linux-with-vultr/ target=_blank rel=noopener>Deploy a Nomad Cluster on Alpine Linux with Vultr</a><a href=https://journal.valeriansaliou.name/>Valerian Saliou</a><div>After spending countless hours trying to understand how to deploy my apps on Kubernetes for the first time to host Mirage, an AI API service that I run, I ended up making myself a promise that the next app I work on would be using a more productive & simpler<li><a href=https://jcs.org/2023/10/25/wifi_da target=_blank rel=noopener>BlueSCSI Wi-Fi Desk Accessory 1.0 Released</a><a href=https://jcs.org/>joshua stein</a><div>BlueSCSI Wi-Fi Desk Accessory
1.0 has been released:
wifi_da-1.0.sit
(StuffIt 3 archive)
SHA256: ccfc9d27dd5da7412d10cef73b81119a1fec3848e4d1d88ff652a07ffdc6a69aSHA1: ff124972f202ceda6d7fa4788110a67ccda6a13a
This is the initial public release of my BlueSCSI Wi-Fi Desk Accessory for
classic MacOS.<li><a href=https://michael.stapelberg.ch/posts/2023-10-25-my-all-flash-zfs-network-storage-build/ target=_blank rel=noopener>My 2023 all-flash ZFS NAS (Network Storage) build</a><a href=https://michael.stapelberg.ch/>Michael Stapelbergs Website</a><div>For over 10 years now, I run two self-built NAS (Network Storage) devices which serve media (currently via Jellyfin) and run daily backups of all my PCs and servers.
In this article, I describe my goals, which hardware I picked for my new build (and why) and how I set it up.
Design Goals
I use my netw…</ul><p>Generated with <a href=https://git.sr.ht/~sircmpwn/openring target=_blank rel=noopener>openring</a>.</section><footer><hr><p><big><strong>Want to comment or have something to add?</strong></big><p>You can write me an email
at <a href=mailto:mitja.felicijan@gmail.com>mitja.felicijan@gmail.com</a> or
catch up with me <a href=https://telegram.me/mitjafelicijan target=_blank>on Telegram</a>.<hr><p>This website does not track you. Content is made available under
the <a href=https://creativecommons.org/licenses/by/4.0/ target=_blank rel=noreferrer>CC BY 4.0 license</a> unless specified
otherwise. Blog is also available as <a href=/index.xml target=_blank>RSS feed</a>.</footer><script>
	 window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };
	</script><script defer src=/_vercel/insights/script.js></script>