Setting up a self-hosted Webdav server for Organice with Caddy
Table of Contents
Originally posted on 16 June 2023.
Last updated on 20 June 2023.
The lazy user's "no fiddling" approach
For the past few years, I have struggled to consult and edit my Org files on my Android phone and on computers from which my Emacs configuration is not easily accessible.
Presumably, the best application for managing Org files on Android seems to be Orgzly. However, when coupled with Syncthing, which synchronises my files between my devices, I often find myself drowning in stale and conflicted files. On top of that, sadly, its development is slow - I am very grateful to its developer for all the work they put into this marvel but, unfortunately, some issues are a deal-breaker for me on the long run.
I used Orgzly for a while, then switched to Metanote, which I find stunning. Unfortunately, the never-ending plague of niche software seems to have stricken again: it doesn't seem actively maintained and, yet again, what at first glance appears to be the most nondescript hitch ends up causing a never-fulfilled wish of resolution. Also, it is not free software.
Long story short, I am now using Organice, which, despite its extensive use of icons, which tends to drive my simple mind into endless loops of forgetfulness, is nice and simple enough.
Organice is a web application, so it cannot (easily) access local files. There seems to be an ongoing effort to implement local storage access for what will eventually be the Android version of Organice, but, for the moment, it relies on external storage backends, namely the proprietary Dropbox service, Gitlab and Webdav.
For the latter, Organice's documentation explains how to set up a Docker container with an instance of Apache providing a Webdav backend. Being rather stupid, I obviously never managed to get it to work in a satisfactory manner because of various issues, the main one being that's I'm too lazy to learn how to set up CORS, which is supposedly necessary when a program hosted on domain A needs to talk to another one hosted on domain B.
To keep things simple and easy, I chose to use Caddy's Webdav plugin and run Organice and the Webdav plugin on the same domain. That way, no fiddling with CORS is involved. The downside is that your Webdav share won't be usable with Organice's official instance, but this shouldn't be a problem, since both your local instance of Organice and its dedicated Webdav share should be up at the same time anyway.
Customising Caddy's Dockerfile
The content of this tutorial is included in Organice's contrib directory.
In this endeavour, we'll be using lucaslorentz' excellent Dockerfile for Caddy.
What makes it particularly elegant is that it maps the labels used in your docker-compose
YAML file to Caddyfile
directives. That way, everything can be neatly defined in your
docker-compose.yml
file. No fiddling with external files involved.
However, we cannot use the upstream Dockerfile direcly, because it does not compile Caddy with Webdav support. We need to modify it and build it locally instead.
cd my-docker-compose-dir
mkdir -p caddy/src/
git clone https://github.com/lucaslorentz/caddy-docker-proxy caddy/src
Now, open caddy/src/Dockerfile
with your favourite text editor (we know which one it is)
and edit it so that it looks like this:
ARG CADDY_VERSION=2.6.4 FROM caddy:${CADDY_VERSION}-builder AS builder RUN xcaddy build \ --with github.com/lucaslorentz/caddy-docker-proxy/v2@v2.8.4 \ --with github.com/mholt/caddy-webdav # --with <additional-plugins> FROM caddy:${CADDY_VERSION}-alpine COPY --from=builder /usr/bin/caddy /usr/bin/caddy CMD ["caddy", "docker-proxy"]
docker-compose.yml
Now, go back to my-docker-compose-dir
and add something like this to docker-compose.yml
:
Please read the comments carefully before using it.
version: "3.8" services: caddy: # image: lucaslorentz/caddy-docker-proxy build: ./caddy/src/ container_name: caddy ports: - 80:80 - 443:443 environment: - CADDY_INGRESS_NETWORKS=caddy volumes: - /var/run/docker.sock:/var/run/docker.sock - ./caddy/caddy-data:/data/ - ./caddy/caddy-config:/config/ # Replace '/path/to/org-dir' with the path to the # directory in which your Org files are stored. - /path/to/org-dir:/srv/dav/Org/ restart: unless-stopped networks: - caddy organice: image: 'twohundredok/organice:latest' container_name: organice # ports: # - '5000:3000' logging: driver: json-file environment: # Replace 'organice.example.org' with your domain. - ORGANICE_WEBDAV_URL=https://organice.example.org/dav # /srv/dav/ volume is bound in caddy container labels: # Replace 'organice.example.org' with your domain. caddy: organice.example.org # Secure your server, either with IP whitelisting, # by uncommenting the next three lines (obviously, # change the value of the IP subnet): # caddy.@blocked.not: "remote_ip 192.168.2.0/24" # caddy.@blocked.path: "*" # caddy.route.respond: "@blocked Forbidden. 403" # Or with HTTP basic auth, by uncommenting the next two # lines, replacing 'myser' with the desired username and # 'mypassword' with a hashed password, which you can # generate with 'docker run -it caddy caddy hash-password'. # Don't forget to escape the dollar signs ('$') in the hash # by doubling them (i.e '$foo$bar' should become '$$foo$$bar'). # caddy.route.basicauth: "*" # caddy.route.basicauth.myuser: mypassword # If you want to restrict the access to the Webdav server # only and keep your Organice instance public, replace the # '*' in the examples above with '/dav/*'. caddy.@proxy.not: "path /dav/*" caddy.route.reverse_proxy: "@proxy {{upstreams 5000}}" caddy.route.rewrite: /dav /dav/ caddy.route.webdav: "/dav/*" caddy.route.webdav.prefix: /dav caddy.route.webdav.root: "/srv/dav/Org" networks: - caddy networks: caddy: name: caddy
We're almost ready now! We're just a few commands away from ataraxia.
docker compose build caddy docker compose up -d
You can now access your Organice instance at https://organice.example.org. Log in with the Webdav backend, and set its URL to https://organice.example.org/dav. If you have set up basic HTTP auth for the Webdav server, enter your credentials, otherwise leave the fields empty.
Related links and acknowledgements
I'd like to thank Marko Kocic (who also seems to be a fan of Emacs), from whom the initial inspiration for this article came: see https://marko.euptera.com/posts/caddy-webdav.html