{"id":13586537,"url":"https://github.com/tomsquest/docker-radicale","last_synced_at":"2025-10-23T19:02:18.296Z","repository":{"id":39600892,"uuid":"70071479","full_name":"tomsquest/docker-radicale","owner":"tomsquest","description":"Docker image for Radicale calendar and contact server :calendar: + security :closed_lock_with_key: + addons :rocket:","archived":false,"fork":false,"pushed_at":"2025-04-05T05:21:41.000Z","size":286,"stargazers_count":710,"open_issues_count":1,"forks_count":86,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-05T06:24:01.805Z","etag":null,"topics":["caldav","caldav-server","carddav","docker","docker-image","radicale"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tomsquest.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-05T15:05:07.000Z","updated_at":"2025-04-05T05:21:43.000Z","dependencies_parsed_at":"2023-11-22T10:29:16.729Z","dependency_job_id":"63e66166-95a6-4914-b5de-fb5e76a3e3c5","html_url":"https://github.com/tomsquest/docker-radicale","commit_stats":null,"previous_names":[],"tags_count":65,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomsquest%2Fdocker-radicale","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomsquest%2Fdocker-radicale/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomsquest%2Fdocker-radicale/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomsquest%2Fdocker-radicale/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tomsquest","download_url":"https://codeload.github.com/tomsquest/docker-radicale/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248615933,"owners_count":21133980,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["caldav","caldav-server","carddav","docker","docker-image","radicale"],"created_at":"2024-08-01T15:05:38.496Z","updated_at":"2025-10-23T19:02:18.290Z","avatar_url":"https://github.com/tomsquest.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"logo.png\" alt=\"Logo\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eDocker-Radicale\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/tomsquest/docker-radicale/actions/workflows/build.yaml\"\u003e\u003cimg src=\"https://github.com/tomsquest/docker-radicale/actions/workflows/build.yaml/badge.svg\" alt=\"Build Status\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/tomsquest/docker-radicale/tags\"\u003e\u003cimg src=\"https://img.shields.io/github/tag/tomsquest/docker-radicale.svg\" alt=\"GitHub tag\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://hub.docker.com/r/tomsquest/docker-radicale/\"\u003e\u003cimg src=\"https://img.shields.io/docker/pulls/tomsquest/docker-radicale.svg\" alt=\"Pulls\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://hub.docker.com/r/tomsquest/docker-radicale/\"\u003e\u003cimg src=\"https://img.shields.io/docker/stars/tomsquest/docker-radicale.svg\" alt=\"Stars\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\nEnhanced Docker image for \u003ca href=\"https://radicale.org\"\u003eRadicale\u003c/a\u003e, the CalDAV/CardDAV server.\n\u003c/p\u003e\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n## Table of contents\n\n- [Features](#features)\n- [Changelog](#changelog)\n- [Versions](#versions)\n  - [Version Tag Format](#version-tag-format)\n- [Architectures](#architectures)\n- [Running](#running)\n  - [Option 1: **Basic** instruction](#option-1-basic-instruction)\n  - [Option 2: **Recommended, Production-grade** instruction (secured, safe...) :rocket:](#option-2-recommended-production-grade-instruction-secured-safe-rocket)\n  - [Docker Compose](#docker-compose)\n- [Custom configuration](#custom-configuration)\n- [Authentication configuration](#authentication-configuration)\n- [Volumes versus Bind-Mounts](#volumes-versus-bind-mounts)\n- [Running with Docker compose](#running-with-docker-compose)\n- [Unraid](#unraid)\n- [Extending the image](#extending-the-image)\n  - [Birthday Calendar](#birthday-calendar)\n- [Versioning with Git](#versioning-with-git)\n- [Custom User/Group ID for the data volume](#custom-usergroup-id-for-the-data-volume)\n  - [Option 0: Do nothing, permission will be fixed by the container itself](#option-0-do-nothing-permission-will-be-fixed-by-the-container-itself)\n  - [Option 1: Create a user/group with id `2999` on the host](#option-1-create-a-usergroup-with-id-2999-on-the-host)\n  - [Option 2: Force the user/group ids on `docker run`](#option-2-force-the-usergroup-ids-on-docker-run)\n  - [Option 3: Build the image with a custom user/group](#option-3-build-the-image-with-a-custom-usergroup)\n- [Running with Podman](#running-with-podman)\n- [Running behind Caddy](#running-behind-caddy)\n- [Contributing](#contributing)\n- [Releasing](#releasing)\n- [Contributors](#contributors)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Features\n\n* :closed_lock_with_key: **Secured**: the container is read-only, with only access to its data dir, and without extraneous privileges\n* :fire: **Safe**: run as a normal user (not root)\n* :building_construction: **Multi-architecture**: run on amd64 and arm64\n* :sparkles: **Batteries included**: \n  * git and ssh included for [versioning](https://github.com/tomsquest/docker-radicale/#versioning-with-git)\n  * Python ldap3 for [LDAP authentication](https://github.com/Kozea/Radicale/wiki/LDAP-authentication)\n  * Python pytz for proper timezone handling\n  * Python bcrypt and argon2 for password hashing\n\n## Changelog\n\n:page_with_curl: See [CHANGELOG.md](CHANGELOG.md)\n\n## Versions\n\nTwo image versions are available:\n\n- **`latest`**: Based on `alpine:3` with **daily** automated builds to include the most recent security patches\n- **Version tags** (e.g., `1.2.3.4`): Specific Radicale versions with image updates\n\n**Which should you use?**\n- Use `tomsquest/docker-radicale:latest` for always-updated security patches (daily build)\n- Use `tomsquest/docker-radicale:$version` for stability with a specific Radicale version\n\n### Version Tag Format\n\nVersion tags follow this format: `[Radicale version].[Image revision]`\n\nFor example, in `1.2.3.4`:\n- `1.2.3` is the Radicale version\n- The final `.4` is our image revision number, incremented for image-specific updates\n\n### Latest version\n\n- `latest` is build every day\n- [Check the Releases page on GitHub](https://github.com/tomsquest/docker-radicale/releases)\n\n## Architectures\n\nThe image is built for two architectures:\n- `amd64`: for your usual server\n- `arm64`: for Raspberry Pi\n\nWhen you run the image, Docker will automatically select the correct image architecture.\n\n## Running\n\n### Option 1: **Basic** instruction\n\n```\ndocker run -d --name radicale \\\n    -p 5232:5232 \\\n    -v ./data:/data \\\n    tomsquest/docker-radicale\n```\n\n### Option 2: **Recommended, Production-grade** instruction (secured, safe...) :rocket:\n\nThis is the most secured instruction:\n\n```\ndocker run -d --name radicale \\\n    -p 127.0.0.1:5232:5232 \\\n    --init \\\n    --read-only \\\n    --security-opt=\"no-new-privileges:true\" \\\n    --cap-drop ALL \\\n    --cap-add CHOWN \\\n    --cap-add SETUID \\\n    --cap-add SETGID \\\n    --cap-add KILL \\\n    --pids-limit 50 \\\n    --memory 256M \\\n    --health-cmd=\"curl --fail http://localhost:5232 || exit 1\" \\\n    --health-interval=30s \\\n    --health-retries=3 \\\n    -v ./data:/data \\\n    tomsquest/docker-radicale\n```\n\nNote on capabilities:\n- `CHOWN` is used to restore the permission of the `data` directory. Remove this if you do not need the `chown` to be run (see [below](#volumes-versus-bind-mounts))\n- `SETUID` and `SETGID` are used to run radicale as the less privileged `radicale` user (with su-exec), and are required.\n- `KILL` is to allow Radicale to exit, and is required.\n\n### Docker Compose\n\nA [Docker compose file](docker-compose.yml) is included.\n\n## Custom configuration\n\nTo change Radicale configuration, first get the config file:\n\n* (Recommended) use this preconfigured [config file](config) from this repository,\n* Or, use [the original Radicale config file](https://raw.githubusercontent.com/Kozea/Radicale/master/config) and:\n  1. set `hosts = 0.0.0.0:5232`\n  2. set `filesystem_folder = /data/collections`\n  3. set `[auth] type = none`\n\nThen:\n1. create a config directory (eg. `mkdir -p /my_custom_config_directory`)\n2. copy your config file into the config folder (e.g. `cp config /my_custom_config_directory/config`)\n3. mount your custom config volume when running the container: `-v /my_custom_config_directory:/config:ro`.\nThe `:ro` at the end make the volume read-only, and is more secured.\n\n## Authentication configuration\n\nThis section shows a basic example of configuring authentication for Radicale using htpasswd with bcrypt algorithm.  \nTo learn more, refer to [the official Radicale document](https://radicale.org/v3.html#auth).\n\nFirst, we need to configure Radicale to use htpasswd authentication and specify htpasswd file's location.  \nCreate a `config` file inside the `config` directory (resulting in the path `config/config`).\n\n```\n[server]\nhosts = 0.0.0.0:5232\n\n[auth]\ntype = htpasswd\nhtpasswd_filename = /config/users\nhtpasswd_encryption = bcrypt\n\n[storage]\nfilesystem_folder = /data/collections\n```\n\nNext, create a `users` file inside the `config` directory (resulting in the path `config/users`).  \nEach line contains the username and bcrypt-hashed password, separated by a colon (`:`).\n\n```\njohn:$2a$10$l1Se4qIaRlfOnaC1pGt32uNe/Dr61r4JrZQCNnY.kTx2KgJ70GPSm\nsarah:$2a$10$lKEHYHjrZ.QHpWQeB/feWe/0m4ZtckLI.cYkVOITW8/0xoLCp1/Wy\n```\n\nFinally, create and run the container using the appropriate volume mount.\nIn this example, both files are stored in the same directory (`./config`).\n\n```bash\ndocker run -d --name radicale tomsquest/docker-radicale \\\n    -p 5232:5232 \\\n    -v ./data:/data \\\n    -v ./config:/config:ro\n```\n\n## Volumes versus Bind-Mounts\n\nThis section is related to the error message `chown: /data: Permission denied`.\n\nWith [Docker volumes](https://docs.docker.com/storage/volumes/), and not [bind-mounts](https://docs.docker.com/storage/bind-mounts/) like shown in the examples above, you may need to disable the container trying to make the `data` directory writable.\n\nThis is done with the `TAKE_FILE_OWNERSHIP` environment variable.  \nThe variable will tell the container to perform or skip the `chown` instruction.  \nThe default value is `true`: the container will try to make the `data` directory writable to the `radicale` user.  \n\nTo disable the `chown`, declare the variable like this:\n\n```\ndocker run -d --name radicale tomsquest/docker-radicale \\\n    -e \"TAKE_FILE_OWNERSHIP=false\"\n```\n\n## Running with Docker compose\n\nA [Docker compose file](docker-compose.yml) is included.  \nIt can also be [extended](https://docs.docker.com/compose/production/#modify-your-compose-file-for-production).  \nMake sure you have Docker compose version 2 or higher.\n\n## Unraid\n\nThis image is compatible with Unraid, and you can find it in the [Community App store](https://unraid.net/community/apps?q=radicale#r).\n\n## Extending the image\n\nThe image is extendable, as per Docker image architecture. You need to create your own `Dockerfile`.\n\nFor example, here is how to add [RadicaleIMAP](https://github.com/Unrud/RadicaleIMAP) (authenticate by email) \nand [RadicaleInfCloud](https://www.inf-it.com/open-source/clients/infcloud/) (an alternative UI) to the image.\n\nPlease note that the [radicale-imap](https://gitlab.com/comzeradd/radicale-imap) plugin is not compatible with\nRadicale 3.0 anymore!\n\nFirst, create a `Dockerfile.extended` (pick the name you want) with this content:\n\n```dockerfile\nFROM tomsquest/docker-radicale\n\nRUN /venv/bin/pip install git+https://github.com/Unrud/RadicaleIMAP\nRUN /venv/bin/pip install git+https://github.com/Unrud/RadicaleInfCloud\n```\n\nThen, build and run it:\n\n```bash\ndocker build -t radicale-extended -f Dockerfile.extended .\ndocker run --name radicale-extended -p 5232:5232 radicale-extended\n```\n\n### Birthday Calendar\n\n[Christof Schulze](https://github.com/christf) provides an extended image to include [Radicale Birthday Calendar](https://github.com/iBigQ/radicale-birthday-calendar).\n\nFind its [project here](https://github.com/christf/docker-radicale-birthday) to add a birthday calendar to your Radicale instance.\n\n## Versioning with Git\n\nRadicale supports a hook which is executed after each change to the CalDAV/CardDAV files.\nThis hook can be used to keep a versions of your CalDAV/CardDAV files through git.\n\nThis image provides `git` to support this feature. \n\nRefer to the [official documentation of Radicale](https://radicale.org/v3.html#versioning-with-git) for the details.\n\n## Custom User/Group ID for the data volume\n\nYou will certainly mount a volume to keep Radicale data between restart/upgrade of the container.\nBut sharing files from the host and the container can be problematic.\nThe reason is that `radicale` user **in** the container does not match the user running the container **on** the host.\n\nTo solve this, this image offers four options (see below for details):\n\n- Option 0: Do nothing, permission will be fixed by the container itself\n- Option 1: Create a user/group with id `2999` on the host\n- Option 2: Force the user/group ids on `docker run`\n- Option 3: Build the image with a custom user/group\n\n### Option 0: Do nothing, permission will be fixed by the container itself\n\nWhen running the container with a /data volume (e.g. `-v ./data:/data`), the container entrypoint will automatically fix the permissions on `/data`.\n\nThis option is OK, but not optimal:\n- Ok for the container, as inside the container, the `radicale` user can read and write its data\n- But on the host, the data directory will then be owned by the user/group 2999:2999\n\n### Option 1: Create a user/group with id `2999` on the host\n\nThe image creates a user and a group with id `2999` in the container.  \nYou can create a user/group on your host matching this id.\n\nExample:\n\n```bash\n# On your host\nsudo addgroup --gid 2999 radicale\nsudo adduser --gid 2999 --uid 2999 --shell /bin/false --disabled-password --no-create-home radicale\n```\n\n### Option 2: Force the user/group ids on `docker run`\n\nThe user and group ids used in the container can be overridden when the container is run.  \nThis is done with the `UID` and `GID` env variables, e.g. `docker run -e UID=123 -e GID=456 ...`.  \nThis will force all operations to be run with this UID/GID.\n\n:warning: The **`--read-only`** run flag cannot be used in this case. \nUsing custom UID/GID tries to modify the filesystem at runtime but this is made **impossible** by the `--read-only` flag.\n\n### Option 3: Build the image with a custom user/group\n\nYou can build the image with custom user and group ids and still use the `--read-only` flag.  \nBut you will have to clone this repo, do a local build and keep up with changes of this image.\n\nUsage: `docker build --build-arg=BUILD_UID=5000 --build-arg=BUILD_GID=5001 ...`.\n\n`BUILD_UID` and `BUILD_GID` are also supported as environment variables to work around a problem on some Synology NAS. See this PR#68.\n\n## Running with Podman\n\nTwo users have given the instructions they used to run the image with Podman:\n- [@greylinux's instructions](https://github.com/tomsquest/docker-radicale/issues/122#issuecomment-1361240992)\n- [@strauss115's instructions](https://github.com/tomsquest/docker-radicale/issues/122#issuecomment-1874607285)\n\n## Running behind Caddy\n\n[Caddy](https://caddyserver.com) is sitting in front of all my self-hosted services, like Radicale.  \nIt brings https and security (basic authentication).\n\nThe following Caddyfile works for me. Note that I don't use Radicale authentication, I have only one user.\n\n```caddyfile\nradicale.yourdomain.com {\n    reverse_proxy 127.0.0.1:5232\n\n    basicauth {\n        tom pas$w0rd\n    }\n}\n```\n\n## Contributing\n\nTo run the tests:\n\n1. `uv sync`\n2. `uv run pytest -v`\n\n## Releasing\n\n1. Create a Git tag, e.g. `1.2.3.4`, push it and the CI will build the images and publish them on Docker hub\n2. Update the `latest` tag\n4. Update `CHANGELOG.md` (after, or in the PR)\n3. Create release on GitHub (`Draft a new release` \u003e pick the tag \u003e `Generate release notes` \u003e `Publish release`)\n\nExample instructions :\n\n```bash\n# Update local tags\ngit fetch --all --tags\n# Create tag\nTAG=1.2.3.4 \u0026\u0026 git tag $TAG \u0026\u0026 git push origin $TAG\n# Draft a new release\nxdg-open https://github.com/tomsquest/docker-radicale/releases/new\n```\n\nNote: the `latest` tag is generated automatically on each push (and daily).\n\n## Contributors\n\n* [Oscar Carlsson](https://github.com/oscarcarlsson): update Radicale to 3.5.5\n* [Emil Miller](https://github.com/realcharmer): update Radicale to 3.3.0\n* [Nate Harris](https://github.com/nwithan8): add image to Unraid community app store\n* [SalaryTheft](https://github.com/SalaryTheft): add section about Authentication configuration\n* [Dillbyrne](https://github.com/dillbyrne): update alpine\n* [Jauder Ho](https://github.com/jauderho): update alpine\n* [Greylinux](https://github.com/Greylinux): running with podman\n* [Tionis](https://github.com/tionis): add openssh for git ssh remotes\n* [flixhsw](https://github.com/flixhsw): support armv7 (Raspberry) and simplify the CI using Docker Buildx\n* [hecd](https://github.com/hecd): fix to run su-exec only when the actual user is root\n* [Jake Mayeux](https://github.com/jakemayeux): change \"data\" folder to `./data` instead of `~/radicale/data` in docker-compose.yml and doc\n* [Thomas](https://github.com/symgryph): reduce image size (/root/.cache) and Alpine upgrade\n* [Bernard Kerckenaere](https://github.com/bernieke): check for read-only container, and help for volumes versus bind-mounts\n* [Dylan Van Assche](https://github.com/DylanVanAssche): hook to read/write to a Git repo\n* [Adzero](https://github.com/adzero): override build args with environment variables\n* [Robert Beal](https://github.com/robertbeal): fixed/configurable userId, versioning...\n* [Loader23](https://github.com/Loader23): config volume idea\n* [Waja](https://github.com/waja): fewer layers is more, InfCloud integration (UI for Radicale) \n* [Christian Burmeister](https://github.com/christianbur): add tzdata to be able to specify timezone \n* [Silas Lenz](https://github.com/silaslenz): add pytz for recurring events\n* [Enno Richter](https://github.com/elohmeier): bcrypt support \n* [Andrew u frank](https://github.com/andrewufrank): house-cleaning of whitespaces in doc \n* [Marcus Kimpenhaus](https://github.com/kimpenhaus): fix for Alpine and https \n* [Thomas Queste](https://github.com/tomsquest): initial image\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomsquest%2Fdocker-radicale","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomsquest%2Fdocker-radicale","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomsquest%2Fdocker-radicale/lists"}