diff --git a/1.1.html b/1.1.html index c33464d3..8c077544 100644 --- a/1.1.html +++ b/1.1.html @@ -5,6 +5,137 @@ @@ -422,7 +555,7 @@
Calendars ¶
Tested with 4.8.3, you need one running on Akonadi for Cal/CarDav support.
The procedure below can also be done trough the sidebar "Calendar Manager". But to ensure it works for everyone this examples uses the menu-bar.
-Settings → Configure KOrganizer
.General → Calendars
.Add
.Radicale configuration ¶
Add/Modify the following section in Radicale main configuration file:
-# Additional HTTP headers
-[headers]
-Access-Control-Allow-Origin = *
-Access-Control-Allow-Methods = GET, POST, OPTIONS, PROPFIND, PROPPATCH, REPORT, PUT, MOVE, DELETE, LOCK, UNLOCK
-Access-Control-Allow-Headers = Authorization, Content-type, Depth, Destination, If-match, If-None-Match, Lock-Token, Overwrite, Prefer, Timeout, User-Agent, X-Client, X-Requested-With
-Access-Control-Expose-Headers = Etag
# Additional HTTP headers
+[headers]
+Access-Control-Allow-Origin = *
+Access-Control-Allow-Methods = GET, POST, OPTIONS, PROPFIND, PROPPATCH, REPORT, PUT, MOVE, DELETE, LOCK, UNLOCK
+Access-Control-Allow-Headers = Authorization, Content-type, Depth, Destination, If-match, If-None-Match, Lock-Token, Overwrite, Prefer, Timeout, User-Agent, X-Client, X-Requested-With
+Access-Control-Expose-Headers = Etag
InfCloud
needs read access for everybody
(including anonymous users) on Radicale's root directory. If using Radicales rights management add the following section to rights file:
# Allow caldavzap, carddavmate and infcloud to work
-[infcloud]
-user: .*
-collection: /
-permission: r
# Allow caldavzap, carddavmate and infcloud to work
+[infcloud]
+user: .*
+collection: /
+permission: r
Additional you need to change [owner-write]
section to use the same syntax for collection as shown in [public]
section.
# Give write access to owners
-[owner-write]
-user: .+
-# collection: ^%(login)s/.+$ # DOES NOT WORK
-collection: ^%(login)s(/.+)?$
-permission: rw
# Give write access to owners
+[owner-write]
+user: .+
+# collection: ^%(login)s/.+$ # DOES NOT WORK
+collection: ^%(login)s(/.+)?$
+permission: rw
InfCloud configuration ¶
Inside InfCloud
configuration file config.js
you need to set globalNetworkCheckSettings
like following example:
// href:
-// put in here your protocol, host and port where Radicale is listening
-// additionalResources:
-// put in here a comma separated list of collections you want additionally look at.
-// Don't forget '' around each collections name
-var globalNetworkCheckSettings={
- href: 'https://host.example.com:5232/',
- hrefLabel: null,
- crossDomain: null,
- additionalResources: ['public'],
- forceReadOnly: null,
- withCredentials: false,
- showHeader: true,
- settingsAccount: true,
- syncInterval: 60000,
- timeOut: 30000,
- lockTimeOut: 10000,
- delegation: false,
- ignoreAlarms: false,
- backgroundCalendars: []
-}
// href:
+// put in here your protocol, host and port where Radicale is listening
+// additionalResources:
+// put in here a comma separated list of collections you want additionally look at.
+// Don't forget '' around each collections name
+var globalNetworkCheckSettings={
+href: 'https://host.example.com:5232/',
+ hrefLabel: null,
+ crossDomain: null,
+ additionalResources: ['public'],
+ forceReadOnly: null,
+ withCredentials: false,
+ showHeader: true,
+ settingsAccount: true,
+ syncInterval: 60000,
+ timeOut: 30000,
+ lockTimeOut: 10000,
+ delegation: false,
+ ignoreAlarms: false,
+ backgroundCalendars: []
+ }
Note
@@ -586,7 +719,7 @@ After installing and accepting it you should restart your browser.
InfCloud
,CardDavMATE
andCalDavZAP
cannot create calendars and/or address books. They need to be created before first login. Each user needs to have minimum of one calendar and/or one adressbook even if only using shared addresses and/or calendars. Client will not login, if the user collections don't exists.Calendars ¶
For iOS devices, the setup is fairly straightforward but there are a few settings that are critical for proper operation.
-+
- From the Home screen, open
Settings
- Select
Mail, Contacts, Calendars
- Select
@@ -607,7 +740,7 @@ After installing and accepting it you should restart your browser.Add Account…
→Other
→Add CalDAV Account
Contacts ¶
In Contacts on iOS 6:
-+
- From the Home screen, open
Settings
- Select
Mail, Contacts, Calendars
- Select
@@ -635,7 +768,7 @@ After installing and accepting it you should restart your browser.Add Account…
→Other
→Add CardDAV Account
Calendars ¶
In iCal 4.0 or iCal 5.0:
-+
- Open the
Preferences
dialog and select theAccounts
tab- Click the
+
button at the lower left to open the account creation wizard- As
@@ -658,7 +791,7 @@ After installing and accepting it you should restart your browser.Account type
selectCalDAV
Contacts ¶
In Contacts 7 (previously known as AddressBook):
-+
- Open the
Preferences
dialog and select theAccounts
tab.- Click the
+
button at the lower left to open the account creation wizard.- As
@@ -681,7 +814,7 @@ After installing and accepting it you should restart your browser.Account type
selectCardDAV
.Nokia / Microsoft Windows Phones ¶
-+
- Go to "Settings" > "email+accounts"
- Click "add an account" > "iCloud"
- Enter random email address (e.g. "foo@bar" and "qwerty") > "sign in"
@@ -713,11 +846,9 @@ After installing and accepting it you should restart your browser.Installing the Server ¶
You can install Radicale thanks to the following command, with superuser rights:
-+python setup.py install -
python setup.py install
Then, launching the server can be easily done by typing as a normal user:
-+radicale -
radicale
Configuring the Server ¶
@@ -728,248 +859,246 @@ After installing and accepting it you should restart your browser.This section is following the latest stable version changes. Please look at the default configuration file included in your package if you have an older version of Radicale.
The server configuration can be modified in /etc/radicale/config
or in ~/.config/radicale/config
. You can use the --config
parameter in the command line to choose a specific path. You can also set the RADICALE_CONFIG
environment variable to a path of your choice. Here is the default configuration file, with the main parameters:
[server]
-
-# CalDAV server hostnames separated by a comma
-# IPv4 syntax: address:port
-# IPv6 syntax: [address]:port
-# For example: 0.0.0.0:9999, [::]:9999
-# IPv6 adresses are configured to only allow IPv6 connections
-#hosts = 0.0.0.0:5232
-
-# Daemon flag
-#daemon = False
-
-# File storing the PID in daemon mode
-#pid =
-
-# SSL flag, enable HTTPS protocol
-#ssl = False
-
-# SSL certificate path
-#certificate = /etc/apache2/ssl/server.crt
-
-# SSL private key
-#key = /etc/apache2/ssl/server.key
-
-# SSL Protocol used. See python's ssl module for available values
-#protocol = PROTOCOL_SSLv23
-
-# Ciphers available. See python's ssl module for available ciphers
-#ciphers =
-
-# Reverse DNS to resolve client address in logs
-#dns_lookup = True
-
-# Root URL of Radicale (starting and ending with a slash)
-#base_prefix = /
-
-# Possibility to allow URLs cleaned by a HTTP server, without the base_prefix
-#can_skip_base_prefix = False
-
-# Message displayed in the client when a password is needed
-#realm = Radicale - Password Required
-
-
-[encoding]
-
-# Encoding for responding requests
-#request = utf-8
-
-# Encoding for storing local collections
-#stock = utf-8
-
-
-[well-known]
-
-# Path where /.well-known/caldav/ is redirected
-#caldav = '/%(user)s/caldav/'
-
-# Path where /.well-known/carddav/ is redirected
-#carddav = '/%(user)s/carddav/'
-
-
-[auth]
-
-# Authentication method
-# Value: None | htpasswd | IMAP | LDAP | PAM | courier | http | remote_user | custom
-#type = None
-
-# Custom authentication handler
-#custom_handler =
-
-# Htpasswd filename
-#htpasswd_filename = /etc/radicale/users
-
-# Htpasswd encryption method
-# Value: plain | sha1 | ssha | crypt | bcrypt | md5
-#htpasswd_encryption = crypt
-
-# LDAP server URL, with protocol and port
-#ldap_url = ldap://localhost:389/
-
-# LDAP base path
-#ldap_base = ou=users,dc=example,dc=com
-
-# LDAP login attribute
-#ldap_attribute = uid
-
-# LDAP filter string
-# placed as X in a query of the form (&(...)X)
-# example: (objectCategory=Person)(objectClass=User)(memberOf=cn=calenderusers,ou=users,dc=example,dc=org)
-# leave empty if no additional filter is needed
-#ldap_filter =
-
-# LDAP dn for initial login, used if LDAP server does not allow anonymous searches
-# Leave empty if searches are anonymous
-#ldap_binddn =
-
-# LDAP password for initial login, used with ldap_binddn
-#ldap_password =
-
-# LDAP scope of the search
-#ldap_scope = OneLevel
-
-# IMAP Configuration
-#imap_hostname = localhost
-#imap_port = 143
-#imap_ssl = False
-
-# PAM group user should be member of
-#pam_group_membership =
-
-# Path to the Courier Authdaemon socket
-#courier_socket =
-
-# HTTP authentication request URL endpoint
-#http_url =
-# POST parameter to use for username
-#http_user_parameter =
-# POST parameter to use for password
-#http_password_parameter =
-
-
-[git]
-
-# Git default options
-#committer = Radicale <radicale@example.com>
-
-
-[rights]
-
-# Rights backend
-# Value: None | authenticated | owner_only | owner_write | from_file | custom
-#type = None
-
-# Custom rights handler
-#custom_handler =
-
-# File for rights management from_file
-#file = ~/.config/radicale/rights
-
-
-[storage]
-
-# Storage backend
-# -------
-# WARNING: ONLY "filesystem" IS DOCUMENTED AND TESTED,
-# OTHER BACKENDS ARE NOT READY FOR PRODUCTION.
-# -------
-# Value: filesystem | multifilesystem | database | custom
-#type = filesystem
-
-# Custom storage handler
-#custom_handler =
-
-# Folder for storing local collections, created if not present
-#filesystem_folder = ~/.config/radicale/collections
-
-# Database URL for SQLAlchemy
-# dialect+driver://user:password@host/dbname[?key=value..]
-# For example: sqlite:///var/db/radicale.db, postgresql://user:password@localhost/radicale
-# See http://docs.sqlalchemy.org/en/rel_0_8/core/engines.html#sqlalchemy.create_engine
-#database_url =
-
-
-[logging]
-
-# Logging configuration file
-# If no config is given, simple information is printed on the standard output
-# For more information about the syntax of the configuration file, see:
-# http://docs.python.org/library/logging.config.html
-#config = /etc/radicale/logging
-# Set the default logging level to debug
-#debug = False
-# Store all environment variables (including those set in the shell)
-#full_environment = False
-
-
-[headers]
-
-# Additional HTTP headers
-#Access-Control-Allow-Origin = *
[server]
+
+# CalDAV server hostnames separated by a comma
+# IPv4 syntax: address:port
+# IPv6 syntax: [address]:port
+# For example: 0.0.0.0:9999, [::]:9999
+# IPv6 adresses are configured to only allow IPv6 connections
+#hosts = 0.0.0.0:5232
+
+# Daemon flag
+#daemon = False
+
+# File storing the PID in daemon mode
+#pid =
+
+# SSL flag, enable HTTPS protocol
+#ssl = False
+
+# SSL certificate path
+#certificate = /etc/apache2/ssl/server.crt
+
+# SSL private key
+#key = /etc/apache2/ssl/server.key
+
+# SSL Protocol used. See python's ssl module for available values
+#protocol = PROTOCOL_SSLv23
+
+# Ciphers available. See python's ssl module for available ciphers
+#ciphers =
+
+# Reverse DNS to resolve client address in logs
+#dns_lookup = True
+
+# Root URL of Radicale (starting and ending with a slash)
+#base_prefix = /
+
+# Possibility to allow URLs cleaned by a HTTP server, without the base_prefix
+#can_skip_base_prefix = False
+
+# Message displayed in the client when a password is needed
+#realm = Radicale - Password Required
+
+
+[encoding]
+
+# Encoding for responding requests
+#request = utf-8
+
+# Encoding for storing local collections
+#stock = utf-8
+
+
+[well-known]
+
+# Path where /.well-known/caldav/ is redirected
+#caldav = '/%(user)s/caldav/'
+
+# Path where /.well-known/carddav/ is redirected
+#carddav = '/%(user)s/carddav/'
+
+
+[auth]
+
+# Authentication method
+# Value: None | htpasswd | IMAP | LDAP | PAM | courier | http | remote_user | custom
+#type = None
+
+# Custom authentication handler
+#custom_handler =
+
+# Htpasswd filename
+#htpasswd_filename = /etc/radicale/users
+
+# Htpasswd encryption method
+# Value: plain | sha1 | ssha | crypt | bcrypt | md5
+#htpasswd_encryption = crypt
+
+# LDAP server URL, with protocol and port
+#ldap_url = ldap://localhost:389/
+
+# LDAP base path
+#ldap_base = ou=users,dc=example,dc=com
+
+# LDAP login attribute
+#ldap_attribute = uid
+
+# LDAP filter string
+# placed as X in a query of the form (&(...)X)
+# example: (objectCategory=Person)(objectClass=User)(memberOf=cn=calenderusers,ou=users,dc=example,dc=org)
+# leave empty if no additional filter is needed
+#ldap_filter =
+
+# LDAP dn for initial login, used if LDAP server does not allow anonymous searches
+# Leave empty if searches are anonymous
+#ldap_binddn =
+
+# LDAP password for initial login, used with ldap_binddn
+#ldap_password =
+
+# LDAP scope of the search
+#ldap_scope = OneLevel
+
+# IMAP Configuration
+#imap_hostname = localhost
+#imap_port = 143
+#imap_ssl = False
+
+# PAM group user should be member of
+#pam_group_membership =
+
+# Path to the Courier Authdaemon socket
+#courier_socket =
+
+# HTTP authentication request URL endpoint
+#http_url =
+# POST parameter to use for username
+#http_user_parameter =
+# POST parameter to use for password
+#http_password_parameter =
+
+
+[git]
+
+# Git default options
+#committer = Radicale <radicale@example.com>
+
+
+[rights]
+
+# Rights backend
+# Value: None | authenticated | owner_only | owner_write | from_file | custom
+#type = None
+
+# Custom rights handler
+#custom_handler =
+
+# File for rights management from_file
+#file = ~/.config/radicale/rights
+
+
+[storage]
+
+# Storage backend
+# -------
+# WARNING: ONLY "filesystem" IS DOCUMENTED AND TESTED,
+# OTHER BACKENDS ARE NOT READY FOR PRODUCTION.
+# -------
+# Value: filesystem | multifilesystem | database | custom
+#type = filesystem
+
+# Custom storage handler
+#custom_handler =
+
+# Folder for storing local collections, created if not present
+#filesystem_folder = ~/.config/radicale/collections
+
+# Database URL for SQLAlchemy
+# dialect+driver://user:password@host/dbname[?key=value..]
+# For example: sqlite:///var/db/radicale.db, postgresql://user:password@localhost/radicale
+# See http://docs.sqlalchemy.org/en/rel_0_8/core/engines.html#sqlalchemy.create_engine
+#database_url =
+
+
+[logging]
+
+# Logging configuration file
+# If no config is given, simple information is printed on the standard output
+# For more information about the syntax of the configuration file, see:
+# http://docs.python.org/library/logging.config.html
+#config = /etc/radicale/logging
+# Set the default logging level to debug
+#debug = False
+# Store all environment variables (including those set in the shell)
+#full_environment = False
+
+
+[headers]
+
+# Additional HTTP headers
+#Access-Control-Allow-Origin = *
This configuration file is read each time the server is launched. If some values are not given, the default ones are used. If no configuration file is available, all the default values are used.
Radicale uses the default logging facility for Python. The default configuration prints the information messages to the standard output. It is possible to print debug messages thanks to:
-radicale --debug
-
+radicale --debug
Radicale can also be configured to send the messages to the console, logging files, syslog, etc. For more information about the syntax of the configuration file, see: http://docs.python.org/library/logging.config.html. Here is an example of logging configuration file:
-# Loggers, handlers and formatters keys
-
-[loggers]
-# Loggers names, main configuration slots
-keys = root
-
-[handlers]
-# Logging handlers, defining logging output methods
-keys = console,file
-
-[formatters]
-# Logging formatters
-keys = simple,full
-
-
-# Loggers
-
-[logger_root]
-# Root logger
-level = DEBUG
-handlers = console,file
-
-
-# Handlers
-
-[handler_console]
-# Console handler
-class = StreamHandler
-level = INFO
-args = (sys.stdout,)
-formatter = simple
-
-[handler_file]
-# File handler
-class = FileHandler
-args = ('/var/log/radicale',)
-formatter = full
-
-
-# Formatters
-
-[formatter_simple]
-# Simple output format
-format = %(message)s
-
-[formatter_full]
-# Full output format
-format = %(asctime)s - %(levelname)s: %(message)s
# Loggers, handlers and formatters keys
+
+[loggers]
+# Loggers names, main configuration slots
+keys = root
+
+[handlers]
+# Logging handlers, defining logging output methods
+keys = console,file
+
+[formatters]
+# Logging formatters
+keys = simple,full
+
+
+# Loggers
+
+[logger_root]
+# Root logger
+level = DEBUG
+handlers = console,file
+
+
+# Handlers
+
+[handler_console]
+# Console handler
+class = StreamHandler
+level = INFO
+args = (sys.stdout,)
+formatter = simple
+
+[handler_file]
+# File handler
+class = FileHandler
+args = ('/var/log/radicale',)
+formatter = full
+
+
+# Formatters
+
+[formatter_simple]
+# Simple output format
+format = %(message)s
+
+[formatter_full]
+# Full output format
+format = %(asctime)s - %(levelname)s: %(message)s
All the options of the server
part can be changed with command line options. These options are available by typing:
radicale --help
-
+radicale --help
To use Radicale with Apache's mod_wsgi
, you first have to install the Radicale module in your Python path and write your .wsgi
file (in /var/www
for example):
import radicale
+
+ radicale.log.start()= radicale.Application() application
Note
The
hosts
,daemon
,pid
,ssl
,certificate
,key
,protocol
andciphers
keys of the[server]
part of the configuration are ignored.
Next you have to create the Apache virtual host (adapt the configuration to your environment):
-<VirtualHost *:80>
- ServerName cal.yourdomain.org
-
- WSGIDaemonProcess radicale user=www-data group=www-data threads=1
- WSGIScriptAlias / /var/www/radicale.wsgi
-
- <Directory /var/www>
- WSGIProcessGroup radicale
- WSGIApplicationGroup %{GLOBAL}
- AllowOverride None
- Order allow,deny
- allow from all
- </Directory>
-</VirtualHost>
<VirtualHost *:80>
+ cal.yourdomain.org
+ ServerName
+=www-data group=www-data threads=1
+ WSGIDaemonProcess radicale user
+ WSGIScriptAlias / /var/www/radicale.wsgi
+<Directory /var/www>
+
+ WSGIProcessGroup radicale
+ WSGIApplicationGroup %{GLOBAL}AllowOverride None
+ Order allow,deny
+ from all
+ allow</Directory>
+ </VirtualHost>
Warning
You should use the root of the (sub)domain (
@@ -1008,29 +1137,29 @@ After installing and accepting it you should restart your browser.WSGIScriptAlias /
), else some CalDAV features may not work.If you want to use authentication with Apache, you really should use one of the Apache authentication modules, instead of the ones from Radicale: they're just better.
Deactivate any rights and module in Radicale and use your favourite Apache authentication backend. You can then restrict the access: allow the
alice
user to access/alice/*
URLs, and everything should work as expected.Here is one example of Apache configuration file:
-+<VirtualHost *:80> - ServerName radicale.local - - WSGIDaemonProcess radicale user=radicale group=radicale threads=1 - WSGIScriptAlias / /usr/share/radicale/radicale.wsgi - - <Directory /usr/share/radicale/> - WSGIProcessGroup radicale - WSGIApplicationGroup %{GLOBAL} - - AuthType Basic - AuthName "Radicale Authentication" - AuthBasicProvider file - AuthUserFile /usr/share/radicale/radicale.passwd - - AllowOverride None - Require valid-user - - RewriteEngine On - RewriteCond %{REMOTE_USER}%{PATH_INFO} !^([^/]+/)\1 - RewriteRule .* - [Forbidden] - </Directory> -</VirtualHost>
<VirtualHost *:80> + radicale.local + ServerName +=radicale group=radicale threads=1 + WSGIDaemonProcess radicale user + WSGIScriptAlias / /usr/share/radicale/radicale.wsgi +<Directory /usr/share/radicale/> + + WSGIProcessGroup radicale + WSGIApplicationGroup %{GLOBAL} +AuthType Basic + "Radicale Authentication" + AuthName file + AuthBasicProvider /usr/share/radicale/radicale.passwd + AuthUserFile +AllowOverride None + valid-user + Require +RewriteEngine On + %{REMOTE_USER}%{PATH_INFO} !^([^/]+/)\1 + RewriteCond .* - [Forbidden] + RewriteRule</Directory> + </VirtualHost>
If you're still convinced that access control is better with Radicale, you have to add
WSGIPassAuthorization On
in your Apache configuration files, as explained in the mod_wsgi documentation.Note
@@ -1085,46 +1214,46 @@ After installing and accepting it you should restart your browser.Section names are only used for naming the rule.
Leading or ending slashes are trimmed from collection's path.
Example:
-+# The default path for this kind of files is ~/.config/radicale/rights -# This can be changed in the configuration file -# -# This file gives independant examples to help users write their own -# configuration files. Using these examples together in the same configuration -# file is meaningless. -# -# The first rule matching both user and collection patterns will be returned. - -# This means all users starting with "admin" may read any collection -[admin] -user: ^admin.*$ -collection: .* -permission: r - -# This means all users may read and write any collection starting with public. -# We do so by just not testing against the user string. -[public] -user: .* -collection: ^public(/.+)?$ -permission: rw - -# A little more complex: give read access to users from a domain for all -# collections of all the users (ie. user@domain.tld can read domain/*). -[domain-wide-access] -user: ^.+@(.+)\..+$ -collection: ^{0}/.+$ -permission: r - -# Allow authenticated user to read all collections -[allow-everyone-read] -user: .+ -collection: .* -permission: r - -# Give write access to owners -[owner-write] -user: .+ -collection: ^%(login)s/.*$ -permission: w
# The default path for this kind of files is ~/.config/radicale/rights +# This can be changed in the configuration file +# +# This file gives independant examples to help users write their own +# configuration files. Using these examples together in the same configuration +# file is meaningless. +# +# The first rule matching both user and collection patterns will be returned. + +# This means all users starting with "admin" may read any collection +[admin] +user: ^admin.*$ +collection: .* +permission: r + +# This means all users may read and write any collection starting with public. +# We do so by just not testing against the user string. +[public] +user: .* +collection: ^public(/.+)?$ +permission: rw + +# A little more complex: give read access to users from a domain for all +# collections of all the users (ie. user@domain.tld can read domain/*). +[domain-wide-access] +user: ^.+@(.+)\..+$ +collection: ^{0}/.+$ +permission: r + +# Allow authenticated user to read all collections +[allow-everyone-read] +user: .+ +collection: .* +permission: r + +# Give write access to owners +[owner-write] +user: .+ +collection: ^%(login)s/.*$ +permission: w
Radicale is available on PyPI. To install, just type as superuser:
-pip install radicale==1.1.*
-
+pip install radicale==1.1.*
If you want the development version of Radicale, take a look at the git repository on GitHub
, or clone it thanks to:
git clone git://github.com/Kozea/Radicale.git
-
+git clone git://github.com/Kozea/Radicale.git
You can also download the Radicale package of the git repository.
Radicale is really easy to install and works out-of-the-box.
-$ python3 -m pip install --upgrade radicale==2.1.*
-$ python3 -m radicale --config "" --storage-filesystem-folder=~/.var/lib/radicale/collections
When your server is launched, you can check that everything's OK by going to http://localhost:5232/ with your browser! You can login with any username and password.
+$ python3 -m pip install --upgrade radicale==2.1.*
+$ python3 -m radicale --config "" --storage-filesystem-folder=~/.var/lib/radicale/collections
When your server is launched, you can check that everything's OK by going to http://localhost:5232/ with your browser! You can login with any username and password.
Want more? Why don't you check our wonderful documentation?
First of all, make sure that python 3.3 or later (python ≥ 3.6 is recommended) and pip are installed. On most distributions it should be enough to install the package python3-pip
.
Then open a console and type:
-# Run the following command as root or
-# add the --user argument to only install for the current user
-$ python3 -m pip install --upgrade radicale==2.1.*
-$ python3 -m radicale --config "" --storage-filesystem-folder=~/.var/lib/radicale/collections
# Run the following command as root or
+# add the --user argument to only install for the current user
+$ python3 -m pip install --upgrade radicale==2.1.*
+$ python3 -m radicale --config "" --storage-filesystem-folder=~/.var/lib/radicale/collections
Victory! Open http://localhost:5232/ in your browser! You can login with any username and password.
The first step is to install Python. Go to python.org and download the latest version of Python 3. Then run the installer. On the first window of the installer, check the "Add Python to PATH" box and click on "Install now". Wait a couple of minutes, it's done!
Launch a command prompt and type:
-C:\Users\User> python -m pip install --upgrade radicale==2.1.*
-C:\Users\User> python -m radicale --config "" --storage-filesystem-folder=~/radicale/collections
:\Users\User> python -m pip install --upgrade radicale==2.1.*
+ C:\Users\User> python -m radicale --config "" --storage-filesystem-folder=~/radicale/collections C
If you are using PowerShell replace --config ""
with --config '""'
.
Victory! Open http://localhost:5232/ in your browser! You can login with any username and password.
The users
file can be created and managed with htpasswd:
# Create a new htpasswd file with the user "user1"
-$ htpasswd -B -c /path/to/users user1
-New password:
-Re-type new password:
-# Add another user
-$ htpasswd -B /path/to/users user2
-New password:
-Re-type new password:
# Create a new htpasswd file with the user "user1"
+$ htpasswd -B -c /path/to/users user1
+New password:
+Re-type new password:
+# Add another user
+$ htpasswd -B /path/to/users user2
+New password:
+Re-type new password:
bcrypt is used to secure the passwords. Radicale requires additional dependencies for this encryption method:
- +$ python3 -m pip install --upgrade radicale[bcrypt]==2.1.*
Authentication can be enabled with the following configuration:
-[auth]
-type = htpasswd
-htpasswd_filename = /path/to/users
-# encryption method used in the htpasswd file
-htpasswd_encryption = bcrypt
[auth]
+type = htpasswd
+htpasswd_filename = /path/to/users
+# encryption method used in the htpasswd file
+htpasswd_encryption = bcrypt
Create the users
file by hand with lines containing the user name and password separated by :
. Example:
user1:password1
-user2:password2
-
+user2:password2
Authentication can be enabled with the following configuration:
-[auth]
-type = htpasswd
-htpasswd_filename = /path/to/users
-# encryption method used in the htpasswd file
-htpasswd_encryption = plain
[auth]
+type = htpasswd
+htpasswd_filename = /path/to/users
+# encryption method used in the htpasswd file
+htpasswd_encryption = plain
The default configuration binds the server to localhost. It can't be reached from other computers. This can be changed with the following configuration options:
- +[server]
+hosts = 0.0.0.0:5232
More addresses can be added (separated by commas).
Data is stored in the folder /var/lib/radicale/collections
. The path can be changed with the following configuration:
[storage]
+filesystem_folder = /path/to/storage
Security: The storage folder should not be readable by unauthorized users. Otherwise, they can read the calendar data and lock the storage. You can find OS dependent instructions in the Running as a service section.
Radicale enforces limits on the maximum number of parallel connections, the maximum file size (important for contacts with big photos) and the rate of incorrect authentication attempts. Connections are terminated after a timeout. The default values should be fine for most scenarios.
-[server]
-max_connections = 20
-# 100 Megabyte
-max_content_length = 100000000
-# 30 seconds
-timeout = 30
-
-[auth]
-# Average delay after failed login attempts in seconds
-delay = 1
[server]
+max_connections = 20
+# 100 Megabyte
+max_content_length = 100000000
+# 30 seconds
+timeout = 30
+
+[auth]
+# Average delay after failed login attempts in seconds
+delay = 1
Create the file ~/.config/systemd/user/radicale.service
:
[Unit]
-Description=A simple CalDAV (calendar) and CardDAV (contact) server
-
-[Service]
-ExecStart=/usr/bin/env python3 -m radicale
-Restart=on-failure
-
-[Install]
-WantedBy=default.target
[Unit]
+Description=A simple CalDAV (calendar) and CardDAV (contact) server
+
+[Service]
+ExecStart=/usr/bin/env python3 -m radicale
+Restart=on-failure
+
+[Install]
+WantedBy=default.target
Radicale will load the configuration file from ~/.config/radicale/config
. You should set the configuration option filesystem_folder
in the storage
section to something like ~/.var/lib/radicale/collections
.
To enable and manage the service run:
-# Enable the service
-$ systemctl --user enable radicale
-# Start the service
-$ systemctl --user start radicale
-# Check the status of the service
-$ systemctl --user status radicale
-# View all log messages
-$ journalctl --user --unit radicale.service
# Enable the service
+$ systemctl --user enable radicale
+# Start the service
+$ systemctl --user start radicale
+# Check the status of the service
+$ systemctl --user status radicale
+# View all log messages
+$ journalctl --user --unit radicale.service
Create the radicale user and group for the Radicale service. (Run useradd --system --home-dir / --shell /sbin/nologin radicale
as root.) The storage folder must be writable by radicale. (Run mkdir -p /var/lib/radicale/collections && chown -R radicale:radicale /var/lib/radicale/collections
as root.)
Security: The storage should not be readable by others. (Run chmod -R o= /var/lib/radicale/collections
as root.)
Create the file /etc/systemd/system/radicale.service
:
[Unit]
-Description=A simple CalDAV (calendar) and CardDAV (contact) server
-After=network.target
-Requires=network.target
-
-[Service]
-ExecStart=/usr/bin/env python3 -m radicale
-Restart=on-failure
-User=radicale
-# Deny other users access to the calendar data
-UMask=0027
-# Optional security settings
-PrivateTmp=true
-ProtectSystem=strict
-ProtectHome=true
-PrivateDevices=true
-ProtectKernelTunables=true
-ProtectKernelModules=true
-ProtectControlGroups=true
-NoNewPrivileges=true
-ReadWritePaths=/var/lib/radicale/collections
-
-[Install]
-WantedBy=multi-user.target
[Unit]
+Description=A simple CalDAV (calendar) and CardDAV (contact) server
+After=network.target
+Requires=network.target
+
+[Service]
+ExecStart=/usr/bin/env python3 -m radicale
+Restart=on-failure
+User=radicale
+# Deny other users access to the calendar data
+UMask=0027
+# Optional security settings
+PrivateTmp=true
+ProtectSystem=strict
+ProtectHome=true
+PrivateDevices=true
+ProtectKernelTunables=true
+ProtectKernelModules=true
+ProtectControlGroups=true
+NoNewPrivileges=true
+ReadWritePaths=/var/lib/radicale/collections
+
+[Install]
+WantedBy=multi-user.target
Radicale will load the configuration file from /etc/radicale/config
.
To enable and manage the service run:
-# Enable the service
-$ systemctl enable radicale
-# Start the service
-$ systemctl start radicale
-# Check the status of the service
-$ systemctl status radicale
-# View all log messages
-$ journalctl --unit radicale.service
# Enable the service
+$ systemctl enable radicale
+# Start the service
+$ systemctl start radicale
+# Check the status of the service
+$ systemctl status radicale
+# View all log messages
+$ journalctl --unit radicale.service
Example Apache configuration:
-RewriteEngine On
-RewriteRule ^/radicale$ /radicale/ [R,L]
-
-<Location "/radicale/">
- ProxyPass http://localhost:5232/ retry=0
- ProxyPassReverse http://localhost:5232/
- RequestHeader set X-Script-Name /radicale/
-</Location>
RewriteEngine On
+ ^/radicale$ /radicale/ [R,L]
+ RewriteRule
+<Location "/radicale/">
+ http://localhost:5232/ retry=0
+ ProxyPass http://localhost:5232/
+ ProxyPassReverse set X-Script-Name /radicale/
+ RequestHeader</Location>
Be reminded that Radicale's default configuration enforces limits on the maximum number of parallel connections, the maximum file size and the rate of incorrect authentication attempts. Connections are terminated after a timeout.
Example Apache configuration:
-RewriteEngine On
-RewriteRule ^/radicale$ /radicale/ [R,L]
-
-<Location "/radicale/">
- AuthType Basic
- AuthName "Radicale - Password Required"
- AuthUserFile "/etc/radicale/htpasswd"
- Require valid-user
-
- ProxyPass http://localhost:5232/ retry=0
- ProxyPassReverse http://localhost:5232/
- RequestHeader set X-Script-Name /radicale/
- RequestHeader set X-Remote-User expr=%{REMOTE_USER}
-</Location>
RewriteEngine On
+ ^/radicale$ /radicale/ [R,L]
+ RewriteRule
+<Location "/radicale/">
+AuthType Basic
+ "Radicale - Password Required"
+ AuthName "/etc/radicale/htpasswd"
+ AuthUserFile valid-user
+ Require
+ http://localhost:5232/ retry=0
+ ProxyPass http://localhost:5232/
+ ProxyPassReverse set X-Script-Name /radicale/
+ RequestHeader set X-Remote-User expr=%{REMOTE_USER}
+ RequestHeader</Location>
Security: Untrusted clients should not be able to access the Radicale server directly. Otherwise, they can authenticate as any user.
SSL certificates can be used to encrypt and authenticate the connection between Radicale and the reverse proxy. First you have to generate a certificate for Radicale and a certificate for the reverse proxy. The following commands generate self-signed certificates. You will be asked to enter additional information about the certificate, the values don't matter and you can keep the defaults.
-$ openssl req -x509 -newkey rsa:4096 -keyout server_key.pem -out server_cert.pem -nodes -days 9999
-$ openssl req -x509 -newkey rsa:4096 -keyout client_key.pem -out client_cert.pem -nodes -days 9999
$ openssl req -x509 -newkey rsa:4096 -keyout server_key.pem -out server_cert.pem -nodes -days 9999
+$ openssl req -x509 -newkey rsa:4096 -keyout client_key.pem -out client_cert.pem -nodes -days 9999
Use the following configuration for Radicale:
-[server]
-ssl = True
-certificate = /path/to/server_cert.pem
-key = /path/to/server_key.pem
-certificate_authority = /path/to/client_cert.pem
[server]
+ssl = True
+certificate = /path/to/server_cert.pem
+key = /path/to/server_key.pem
+certificate_authority = /path/to/client_cert.pem
Example nginx configuration:
location /radicale/ {
proxy_pass https://localhost:5232/;
@@ -740,8 +870,7 @@ user2:password2
proxy_ssl_certificate /path/to/client_cert.pem;
proxy_ssl_certificate_key /path/to/client_key.pem;
proxy_ssl_trusted_certificate /path/to/server_cert.pem;
-}
-
+}
Be reminded that Radicale's default configuration enforces limits on the maximum upload file size.
Security: The None
authentication type disables all rights checking. Don't use it with REMOTE_USER
. Use remote_user
instead.
Example uWSGI configuration:
-[uwsgi]
-http-socket = 127.0.0.1:5232
-processes = 8
-plugin = python3
-module = radicale
-env = RADICALE_CONFIG=/etc/radicale/config
[uwsgi]
+http-socket = 127.0.0.1:5232
+processes = 8
+plugin = python3
+module = radicale
+env = RADICALE_CONFIG=/etc/radicale/config
Example Gunicorn configuration:
- +gunicorn --bind '127.0.0.1:5232' --workers 8 --env 'RADICALE_CONFIG=/etc/radicale/config' radicale
Set the configuration option type
in the auth
section to remote_user
. Radicale uses the user name provided by the WSGI server and disables authentication over HTTP.
The repository must be initialized by running git init
in the file system folder. Internal files of Radicale can be excluded by creating the file .gitignore
with the following content:
.Radicale.cache
.Radicale.lock
-.Radicale.tmp-*
-
+.Radicale.tmp-*
The configuration option hook
in the storage
section must be set to the following command:
git add -A && (git diff --cached --quiet || git commit -m "Changes by "%(user)s)
The command gets executed after every change to the storage and commits the changes into the git repository.
To create a new collection, you have to create the corresponding folder in the file system storage (e.g. collection-root/user/calendar
). To tell Radicale and clients that the collection is a calendar, you have to create the file .Radicale.props
with the following content in the folder:
{"tag": "VCALENDAR"}
The calendar is now available at the URL path /user/calendar
. For address books the file must contain:
{"tag": "VADDRESSBOOK"}
Calendar and address book collections must not have any child collections. Clients with automatic discovery of collections will only show calendars and addressbooks that are direct children of the path /USERNAME/
.
Delete collections by deleting the corresponding folders.
To create a new calendar run something like:
-$ curl -u user -X MKCOL 'http://localhost:5232/user/calendar' --data \
-'<?xml version="1.0" encoding="UTF-8" ?>
-<create xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:I="http://apple.com/ns/ical/">
- <set>
- <prop>
- <resourcetype>
- <collection />
- <C:calendar />
- </resourcetype>
- <C:supported-calendar-component-set>
- <C:comp name="VEVENT" />
- <C:comp name="VJOURNAL" />
- <C:comp name="VTODO" />
- </C:supported-calendar-component-set>
- <displayname>Calendar</displayname>
- <C:calendar-description>Example calendar</C:calendar-description>
- <I:calendar-color>#ff0000ff</I:calendar-color>
- </prop>
- </set>
-</create>'
$ curl -u user -X MKCOL 'http://localhost:5232/user/calendar' --data \
+'<?xml version="1.0" encoding="UTF-8" ?>
+<create xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:I="http://apple.com/ns/ical/">
+ <set>
+ <prop>
+ <resourcetype>
+ <collection />
+ <C:calendar />
+ </resourcetype>
+ <C:supported-calendar-component-set>
+ <C:comp name="VEVENT" />
+ <C:comp name="VJOURNAL" />
+ <C:comp name="VTODO" />
+ </C:supported-calendar-component-set>
+ <displayname>Calendar</displayname>
+ <C:calendar-description>Example calendar</C:calendar-description>
+ <I:calendar-color>#ff0000ff</I:calendar-color>
+ </prop>
+ </set>
+</create>'
To create a new address book run something like:
-$ curl -u user -X MKCOL 'http://localhost:5232/user/addressbook' --data \
-'<?xml version="1.0" encoding="UTF-8" ?>
-<create xmlns="DAV:" xmlns:CR="urn:ietf:params:xml:ns:carddav">
- <set>
- <prop>
- <resourcetype>
- <collection />
- <CR:addressbook />
- </resourcetype>
- <displayname>Address book</displayname>
- <CR:addressbook-description>Example address book</CR:addressbook-description>
- </prop>
- </set>
-</create>'
$ curl -u user -X MKCOL 'http://localhost:5232/user/addressbook' --data \
+'<?xml version="1.0" encoding="UTF-8" ?>
+<create xmlns="DAV:" xmlns:CR="urn:ietf:params:xml:ns:carddav">
+ <set>
+ <prop>
+ <resourcetype>
+ <collection />
+ <CR:addressbook />
+ </resourcetype>
+ <displayname>Address book</displayname>
+ <CR:addressbook-description>Example address book</CR:addressbook-description>
+ </prop>
+ </set>
+</create>'
The collection /USERNAME
will be created automatically, when the user authenticates to Radicale for the first time. Clients with automatic discovery of collections will only show calendars and address books that are direct children of the path /USERNAME/
.
Delete the collections by running something like:
- +$ curl -u user -X DELETE 'http://localhost:5232/user/calendar'
Radicale can be configured with a configuration file or with command line arguments.
An example configuration file looks like:
-[server]
-# Bind all addresses
-hosts = 0.0.0.0:5232
-
-[auth]
-type = htpasswd
-htpasswd_filename = /path/to/users
-htpasswd_encryption = bcrypt
-[storage]
-filesystem_folder = ~/.var/lib/radicale/collections
[server]
+# Bind all addresses
+hosts = 0.0.0.0:5232
+
+[auth]
+type = htpasswd
+htpasswd_filename = /path/to/users
+htpasswd_encryption = bcrypt
+[storage]
+filesystem_folder = ~/.var/lib/radicale/collections
Radicale tries to load configuration files from /etc/radicale/config
, ~/.config/radicale/config
and the RADICALE_CONFIG
environment variable. This behaviour can be overwritten by specifying a path with the --config /path/to/config
command line argument.
The same example configuration via command line arguments looks like:
-python3 -m radicale --config "" --server-hosts 0.0.0.0:5232 --auth-type htpasswd --htpasswd-filename /path/to/htpasswd --htpasswd-encryption bcrypt
python3 -m radicale --config "" --server-hosts 0.0.0.0:5232 --auth-type htpasswd --htpasswd-filename /path/to/htpasswd --htpasswd-encryption bcrypt
The --config ""
argument is required to stop Radicale from trying to load configuration files. Run python3 -m radicale --help
for more information.
In the following, all configuration categories and options are described.
Available methods:
plain
: Passwords are stored in plaintext. This is obviously not secure! The htpasswd file for this can be created by hand and looks like:
user1:password1
-user2:password2
-
+user2:password2
bcrypt
: This uses a modified version of the Blowfish stream cipher. It's very secure. The passlib python module is required for this. Additionally you may need one of the following python modules: bcrypt, py-bcrypt or bcryptor.
md5
: This uses an iterated md5 digest of the password with a salt. The passlib python module is required for this.
sha1
: Passwords are stored as SHA1 hashes. It's insecure!
In this section additional HTTP headers that are sent to clients can be specified.
An example to relax the same-origin policy:
- +Access-Control-Allow-Origin = *
This page describes the format of the rights file for the from_file
authentication backend. The configuration option file
in the rights
section must point to the rights file.
The recommended rights method is owner_only
. If access to calendars and address books outside of the home directory of users (that's /USERNAME/
) is granted, clients won't detect these collections and will not show them to the user. This is only useful if you access calendars and address books directly via URL.
An example rights file:
-# The user "admin" can read and write any collection.
-[admin]
-user = admin
-collection = .*
-permission = rw
-
-# Block access for the user "user" to everything.
-[block]
-user = user
-collection = .*
-permission =
-
-# Authenticated users can read and write their own collections.
-[owner-write]
-user = .+
-collection = %(login)s(/.*)?
-permission = rw
-
-# Everyone can read the root collection
-[read]
-user = .*
-collection =
-permission = r
# The user "admin" can read and write any collection.
+[admin]
+user = admin
+collection = .*
+permission = rw
+
+# Block access for the user "user" to everything.
+[block]
+user = user
+collection = .*
+permission =
+
+# Authenticated users can read and write their own collections.
+[owner-write]
+user = .+
+collection = %(login)s(/.*)?
+permission = rw
+
+# Everyone can read the root collection
+[read]
+user = .*
+collection =
+permission = r
The titles of the sections are ignored (but must be unique). The keys user
and collection
contain regular expressions, that are matched against the user name and the path of the collection. Permissions from the first matching section are used. If no section matches, access gets denied.
The user name is empty for anonymous users. Therefore, the regex .+
only matches authenticated users and .*
matches everyone (including anonymous users).
The path of the collection is separated by /
and has no leading or trailing /
. Therefore, the path of the root collection is empty.
Use the flock utility.
-# Exclusive
-$ flock --exclusive /path/to/storage/.Radicale.lock COMMAND
-# Shared
-$ flock --shared /path/to/storage/.Radicale.lock COMMAND
# Exclusive
+$ flock --exclusive /path/to/storage/.Radicale.lock COMMAND
+# Shared
+$ flock --shared /path/to/storage/.Radicale.lock COMMAND
An example configuration to write the log output to the file /var/log/radicale/log
:
[loggers]
-keys = root
-
-[handlers]
-keys = file
-
-[formatters]
-keys = full
-
-[logger_root]
-# Change this to DEBUG or INFO for higher verbosity.
-level = WARNING
-handlers = file
-
-[handler_file]
-class = FileHandler
-# Specify the output file here.
-args = ('/var/log/radicale/log',)
-formatter = full
-
-[formatter_full]
-format = %(asctime)s - [%(thread)x] %(levelname)s: %(message)s
[loggers]
+keys = root
+
+[handlers]
+keys = file
+
+[formatters]
+keys = full
+
+[logger_root]
+# Change this to DEBUG or INFO for higher verbosity.
+level = WARNING
+handlers = file
+
+[handler_file]
+class = FileHandler
+# Specify the output file here.
+args = ('/var/log/radicale/log',)
+formatter = full
+
+[formatter_full]
+format = %(asctime)s - [%(thread)x] %(levelname)s: %(message)s
You can specify multiple logger, handler and formatter if you want to have multiple simultaneous log outputs.
The parent folder of the log files must exist and must be writable by Radicale.
Security: The log files should not be readable by unauthorized users. Set permissions accordingly.
An example handler configuration to write the log output to the file /var/log/radicale/log
and rotate it. Replace the section handler_file
from the file logging example:
[handler_file]
-class = handlers.TimedRotatingFileHandler
-# Specify the output file and parameter for rotation here.
-# See https://docs.python.org/3/library/logging.handlers.html#logging.handlers.TimedRotatingFileHandler
-# Example: rollover at midnight and keep 7 files (means one week)
-args = ('/var/log/radicale/log', 'midnight', 1, 7)
-formatter = full
[handler_file]
+class = handlers.TimedRotatingFileHandler
+# Specify the output file and parameter for rotation here.
+# See https://docs.python.org/3/library/logging.handlers.html#logging.handlers.TimedRotatingFileHandler
+# Example: rollover at midnight and keep 7 files (means one week)
+args = ('/var/log/radicale/log', 'midnight', 1, 7)
+formatter = full
An example handler configuration to write the log output to the file /var/log/radicale/log
and rotate it . Replace the section handle_file
from the file logging example:
[handler_file]
-class = handlers.RotatingFileHandler
-# Specify the output file and parameter for rotation here.
-# See https://docs.python.org/3/library/logging.handlers.html#logging.handlers.RotatingFileHandler
-# Example: rollover at 100000 kB and keep 10 files (means 1 MB)
-args = ('/var/log/radicale/log', 'a', 100000, 10)
-formatter = full
[handler_file]
+class = handlers.RotatingFileHandler
+# Specify the output file and parameter for rotation here.
+# See https://docs.python.org/3/library/logging.handlers.html#logging.handlers.RotatingFileHandler
+# Example: rollover at 100000 kB and keep 10 files (means 1 MB)
+args = ('/var/log/radicale/log', 'a', 100000, 10)
+formatter = full
To get started we walk through the creation of a simple authentication plugin, that accepts login attempts if the username and password are equal.
The easiest way to develop and install python modules is Distutils. For a minimal setup create the file setup.py
with the following content in an empty folder:
#!/usr/bin/env python3
-
-from distutils.core import setup
-
-setup(name="radicale_silly_auth", packages=["radicale_silly_auth"])
#!/usr/bin/env python3
+
+from distutils.core import setup
+
+="radicale_silly_auth", packages=["radicale_silly_auth"]) setup(name
In the same folder create the sub-folder radicale_silly_auth
. The folder must have the same name as specified in packages
above.
Create the file __init__.py
in the radicale_silly_auth
folder with the following content:
from radicale.auth import BaseAuth
-
-
-class Auth(BaseAuth):
- def is_authenticated(self, user, password):
- # Example custom configuration option
- foo = ""
- if self.configuration.has_option("auth", "foo"):
- foo = self.configuration.get("auth", "foo")
- self.logger.info("Configuration option %r is %r", "foo", foo)
-
- # Check authentication
- self.logger.info("Login attempt by %r with password %r",
- user, password)
- return user == password
from radicale.auth import BaseAuth
+
+
+class Auth(BaseAuth):
+def is_authenticated(self, user, password):
+ # Example custom configuration option
+ = ""
+ foo if self.configuration.has_option("auth", "foo"):
+ = self.configuration.get("auth", "foo")
+ foo self.logger.info("Configuration option %r is %r", "foo", foo)
+
+# Check authentication
+ self.logger.info("Login attempt by %r with password %r",
+
+ user, password)return user == password
Install the python module by running the following command in the same folder as setup.py
:
python3 -m pip install --upgrade .
To make use this great creation in Radicale, set the configuration option type
in the auth
section to radicale_silly_auth
:
[auth]
+type = radicale_silly_auth
+foo = bar
You can uninstall the module with:
- +python3 -m pip uninstall radicale_silly_auth
If you import big calendars or address books into Radicale 2.x.x the first request might take a long time, because it has to initialize its internal caches. Clients can time out, subsequent requests will be much faster.
You can check the imported storage for errors by starting Radicale >= 2.1.5 with the --verify-storage
argument.
You can install version 1.1.x with:
- +$ python3 -m pip install --upgrade radicale==1.1.*
Radicale is available on PyPI. To install, just type as superuser:
-$ python3 -m pip install --upgrade radicale==2.1.*
-
+$ python3 -m pip install --upgrade radicale==2.1.*
If you want the development version of Radicale, take a look at the git repository on GitHub, or install it directly with:
-$ python3 -m pip install --upgrade git+https://github.com/Kozea/Radicale
-
+$ python3 -m pip install --upgrade git+https://github.com/Kozea/Radicale
You can also download the content of the repository as an archive.
Radicale is really easy to install and works out-of-the-box.
-$ python3 -m pip install --upgrade radicale
-$ python3 -m radicale --storage-filesystem-folder=~/.var/lib/radicale/collections
When the server is launched, open http://localhost:5232/ in your browser! You can login with any username and password.
+$ python3 -m pip install --upgrade radicale
+$ python3 -m radicale --storage-filesystem-folder=~/.var/lib/radicale/collections
When the server is launched, open http://localhost:5232/ in your browser! You can login with any username and password.
Want more? Check the tutorials and the documentation.
First, make sure that python 3.5 or later (python ≥ 3.6 is recommended) and pip are installed. On most distributions it should be enough to install the package python3-pip
.
Then open a console and type:
-# Run the following command as root or
-# add the --user argument to only install for the current user
-$ python3 -m pip install --upgrade radicale
-$ python3 -m radicale --storage-filesystem-folder=~/.var/lib/radicale/collections
Victory! Open http://localhost:5232/ in your browser! You can log in with any username and password.
+# Run the following command as root or
+# add the --user argument to only install for the current user
+$ python3 -m pip install --upgrade radicale
+$ python3 -m radicale --storage-filesystem-folder=~/.var/lib/radicale/collections
Victory! Open http://localhost:5232/ in your browser! You can log in with any username and password.
The first step is to install Python. Go to python.org and download the latest version of Python 3. Then run the installer. On the first window of the installer, check the "Add Python to PATH" box and click on "Install now". Wait a couple of minutes, it's done!
Launch a command prompt and type:
-C:\Users\User> python -m pip install --upgrade radicale
-C:\Users\User> python -m radicale --storage-filesystem-folder=~/radicale/collections
Victory! Open http://localhost:5232/ in your browser! You can log in with any username and password.
+:\Users\User> python -m pip install --upgrade radicale
+ C:\Users\User> python -m radicale --storage-filesystem-folder=~/radicale/collections C
Victory! Open http://localhost:5232/ in your browser! You can log in with any username and password.
The users
file can be created and managed with htpasswd:
# Create a new htpasswd file with the user "user1"
-$ htpasswd -c /path/to/users user1
-New password:
-Re-type new password:
-# Add another user
-$ htpasswd /path/to/users user2
-New password:
-Re-type new password:
# Create a new htpasswd file with the user "user1"
+$ htpasswd -c /path/to/users user1
+New password:
+Re-type new password:
+# Add another user
+$ htpasswd /path/to/users user2
+New password:
+Re-type new password:
Authentication can be enabled with the following configuration:
-[auth]
-type = htpasswd
-htpasswd_filename = /path/to/users
-# encryption method used in the htpasswd file
-htpasswd_encryption = md5
[auth]
+type = htpasswd
+htpasswd_filename = /path/to/users
+# encryption method used in the htpasswd file
+htpasswd_encryption = md5
Create the users
file by hand with lines containing the user name and password separated by :
. Example:
user1:password1
-user2:password2
-
+user2:password2
Authentication can be enabled with the following configuration:
-[auth]
-type = htpasswd
-htpasswd_filename = /path/to/users
-# encryption method used in the htpasswd file
-htpasswd_encryption = plain
[auth]
+type = htpasswd
+htpasswd_filename = /path/to/users
+# encryption method used in the htpasswd file
+htpasswd_encryption = plain
The default configuration binds the server to localhost. It can't be reached from other computers. This can be changed with the following configuration options (IPv4 and IPv6):
- +[server]
+hosts = 0.0.0.0:5232, [::]:5232
Data is stored in the folder /var/lib/radicale/collections
. The path can be changed with the following configuration:
[storage]
+filesystem_folder = /path/to/storage
@@ -340,16 +472,16 @@ user2:password2Security: The storage folder should not be readable by unauthorized users. Otherwise, they can read the calendar data and lock the storage. You can find OS dependent instructions in the Running as a service section.
Radicale enforces limits on the maximum number of parallel connections, the maximum file size (important for contacts with big photos) and the rate of incorrect authentication attempts. Connections are terminated after a timeout. The default values should be fine for most scenarios.
-[server]
-max_connections = 20
-# 100 Megabyte
-max_content_length = 100000000
-# 30 seconds
-timeout = 30
-
-[auth]
-# Average delay after failed login attempts in seconds
-delay = 1
[server]
+max_connections = 20
+# 100 Megabyte
+max_content_length = 100000000
+# 30 seconds
+timeout = 30
+
+[auth]
+# Average delay after failed login attempts in seconds
+delay = 1
Security: The storage should not be readable by others. (Run chmod -R o= /var/lib/radicale/collections
as root.)
Create the file /etc/systemd/system/radicale.service
:
[Unit]
-Description=A simple CalDAV (calendar) and CardDAV (contact) server
-After=network.target
-Requires=network.target
-
-[Service]
-ExecStart=/usr/bin/env python3 -m radicale
-Restart=on-failure
-User=radicale
-# Deny other users access to the calendar data
-UMask=0027
-# Optional security settings
-PrivateTmp=true
-ProtectSystem=strict
-ProtectHome=true
-PrivateDevices=true
-ProtectKernelTunables=true
-ProtectKernelModules=true
-ProtectControlGroups=true
-NoNewPrivileges=true
-ReadWritePaths=/var/lib/radicale/collections
-
-[Install]
-WantedBy=multi-user.target
[Unit]
+Description=A simple CalDAV (calendar) and CardDAV (contact) server
+After=network.target
+Requires=network.target
+
+[Service]
+ExecStart=/usr/bin/env python3 -m radicale
+Restart=on-failure
+User=radicale
+# Deny other users access to the calendar data
+UMask=0027
+# Optional security settings
+PrivateTmp=true
+ProtectSystem=strict
+ProtectHome=true
+PrivateDevices=true
+ProtectKernelTunables=true
+ProtectKernelModules=true
+ProtectControlGroups=true
+NoNewPrivileges=true
+ReadWritePaths=/var/lib/radicale/collections
+
+[Install]
+WantedBy=multi-user.target
Radicale will load the configuration file from /etc/radicale/config
.
To enable and manage the service run:
-# Enable the service
-$ systemctl enable radicale
-# Start the service
-$ systemctl start radicale
-# Check the status of the service
-$ systemctl status radicale
-# View all log messages
-$ journalctl --unit radicale.service
# Enable the service
+$ systemctl enable radicale
+# Start the service
+$ systemctl start radicale
+# Check the status of the service
+$ systemctl status radicale
+# View all log messages
+$ journalctl --unit radicale.service
Create the file ~/.config/systemd/user/radicale.service
:
[Unit]
-Description=A simple CalDAV (calendar) and CardDAV (contact) server
-
-[Service]
-ExecStart=/usr/bin/env python3 -m radicale
-Restart=on-failure
-
-[Install]
-WantedBy=default.target
[Unit]
+Description=A simple CalDAV (calendar) and CardDAV (contact) server
+
+[Service]
+ExecStart=/usr/bin/env python3 -m radicale
+Restart=on-failure
+
+[Install]
+WantedBy=default.target
Radicale will load the configuration file from ~/.config/radicale/config
. You should set the configuration option filesystem_folder
in the storage
section to something like ~/.var/lib/radicale/collections
.
To enable and manage the service run:
-# Enable the service
-$ systemctl --user enable radicale
-# Start the service
-$ systemctl --user start radicale
-# Check the status of the service
-$ systemctl --user status radicale
-# View all log messages
-$ journalctl --user --unit radicale.service
# Enable the service
+$ systemctl --user enable radicale
+# Start the service
+$ systemctl --user start radicale
+# Check the status of the service
+$ systemctl --user status radicale
+# View all log messages
+$ journalctl --user --unit radicale.service
Example Apache configuration:
-RewriteEngine On
-RewriteRule ^/radicale$ /radicale/ [R,L]
-
-<Location "/radicale/">
- ProxyPass http://localhost:5232/ retry=0
- ProxyPassReverse http://localhost:5232/
- RequestHeader set X-Script-Name /radicale
-</Location>
RewriteEngine On
+ ^/radicale$ /radicale/ [R,L]
+ RewriteRule
+<Location "/radicale/">
+ http://localhost:5232/ retry=0
+ ProxyPass http://localhost:5232/
+ ProxyPassReverse set X-Script-Name /radicale
+ RequestHeader</Location>
Example Apache .htaccess configuration:
-DirectoryIndex disabled
-RewriteEngine On
-RewriteRule ^(.*)$ http://localhost:5232/$1 [P,L]
-
-# Set to directory of .htaccess file:
-RequestHeader set X-Script-Name /radicale
disabled
+ DirectoryIndexRewriteEngine On
+ ^(.*)$ http://localhost:5232/$1 [P,L]
+ RewriteRule
+# Set to directory of .htaccess file:
+ set X-Script-Name /radicale RequestHeader
Be reminded that Radicale's default configuration enforces limits on the maximum number of parallel connections, the maximum file size and the rate of incorrect authentication attempts. Connections are terminated after a timeout.
Example Apache configuration:
-RewriteEngine On
-RewriteRule ^/radicale$ /radicale/ [R,L]
-
-<Location "/radicale/">
- AuthType Basic
- AuthName "Radicale - Password Required"
- AuthUserFile "/etc/radicale/htpasswd"
- Require valid-user
-
- ProxyPass http://localhost:5232/ retry=0
- ProxyPassReverse http://localhost:5232/
- RequestHeader set X-Script-Name /radicale
- RequestHeader set X-Remote-User expr=%{REMOTE_USER}
-</Location>
RewriteEngine On
+ ^/radicale$ /radicale/ [R,L]
+ RewriteRule
+<Location "/radicale/">
+AuthType Basic
+ "Radicale - Password Required"
+ AuthName "/etc/radicale/htpasswd"
+ AuthUserFile valid-user
+ Require
+ http://localhost:5232/ retry=0
+ ProxyPass http://localhost:5232/
+ ProxyPassReverse set X-Script-Name /radicale
+ RequestHeader set X-Remote-User expr=%{REMOTE_USER}
+ RequestHeader</Location>
Example Apache .htaccess configuration:
-DirectoryIndex disabled
-RewriteEngine On
-RewriteRule ^(.*)$ http://localhost:5232/$1 [P,L]
-
-AuthType Basic
-AuthName "Radicale - Password Required"
-AuthUserFile "/etc/radicale/htpasswd"
-Require valid-user
-
-# Set to directory of .htaccess file:
-RequestHeader set X-Script-Name /radicale
-RequestHeader set X-Remote-User expr=%{REMOTE_USER}
disabled
+ DirectoryIndexRewriteEngine On
+ ^(.*)$ http://localhost:5232/$1 [P,L]
+ RewriteRule
+AuthType Basic
+ "Radicale - Password Required"
+ AuthName "/etc/radicale/htpasswd"
+ AuthUserFile valid-user
+ Require
+# Set to directory of .htaccess file:
+ set X-Script-Name /radicale
+ RequestHeader set X-Remote-User expr=%{REMOTE_USER} RequestHeader
@@ -520,14 +650,14 @@ user2:password2Security: Untrusted clients should not be able to access the Radicale server directly. Otherwise, they can authenticate as any user.
SSL certificates can be used to encrypt and authenticate the connection between Radicale and the reverse proxy. First you have to generate a certificate for Radicale and a certificate for the reverse proxy. The following commands generate self-signed certificates. You will be asked to enter additional information about the certificate, the values don't matter and you can keep the defaults.
-$ openssl req -x509 -newkey rsa:4096 -keyout server_key.pem -out server_cert.pem -nodes -days 9999
-$ openssl req -x509 -newkey rsa:4096 -keyout client_key.pem -out client_cert.pem -nodes -days 9999
$ openssl req -x509 -newkey rsa:4096 -keyout server_key.pem -out server_cert.pem -nodes -days 9999
+$ openssl req -x509 -newkey rsa:4096 -keyout client_key.pem -out client_cert.pem -nodes -days 9999
Use the following configuration for Radicale:
-[server]
-ssl = True
-certificate = /path/to/server_cert.pem
-key = /path/to/server_key.pem
-certificate_authority = /path/to/client_cert.pem
[server]
+ssl = True
+certificate = /path/to/server_cert.pem
+key = /path/to/server_key.pem
+certificate_authority = /path/to/client_cert.pem
Example nginx configuration:
location /radicale/ {
proxy_pass https://localhost:5232/;
@@ -536,8 +666,7 @@ user2:password2
proxy_ssl_certificate /path/to/client_cert.pem;
proxy_ssl_certificate_key /path/to/client_key.pem;
proxy_ssl_trusted_certificate /path/to/server_cert.pem;
-}
-
+}
Radicale is compatible with the WSGI specification.
A configuration file can be set with the RADICALE_CONFIG
environment variable, otherwise no configuration file is loaded and the default configuration is used.
Example uWSGI configuration:
-[uwsgi]
-http-socket = 127.0.0.1:5232
-processes = 8
-plugin = python3
-module = radicale
-env = RADICALE_CONFIG=/etc/radicale/config
[uwsgi]
+http-socket = 127.0.0.1:5232
+processes = 8
+plugin = python3
+module = radicale
+env = RADICALE_CONFIG=/etc/radicale/config
Example Gunicorn configuration:
- +gunicorn --bind '127.0.0.1:5232' --workers 8 --env 'RADICALE_CONFIG=/etc/radicale/config' radicale
Set the configuration option type
in the auth
section to remote_user
. Radicale uses the user name provided by the WSGI server and disables authentication over HTTP.
The repository must be initialized by running git init
in the file system folder. Internal files of Radicale can be excluded by creating the file .gitignore
with the following content:
.Radicale.cache
.Radicale.lock
-.Radicale.tmp-*
-
+.Radicale.tmp-*
The configuration option hook
in the storage
section must be set to the following command:
git add -A && (git diff --cached --quiet || git commit -m "Changes by "%(user)s)
The command gets executed after every change to the storage and commits the changes into the git repository.
Radicale can be configured with a configuration file or with command line arguments.
An example configuration file looks like:
-[server]
-# Bind all addresses
-hosts = 0.0.0.0:5232, [::]:5232
-
-[auth]
-type = htpasswd
-htpasswd_filename = ~/.config/radicale/users
-htpasswd_encryption = md5
-
-[storage]
-filesystem_folder = ~/.var/lib/radicale/collections
[server]
+# Bind all addresses
+hosts = 0.0.0.0:5232, [::]:5232
+
+[auth]
+type = htpasswd
+htpasswd_filename = ~/.config/radicale/users
+htpasswd_encryption = md5
+
+[storage]
+filesystem_folder = ~/.var/lib/radicale/collections
Radicale tries to load configuration files from /etc/radicale/config
and ~/.config/radicale/config
. Custom paths can be specified with the --config /path/to/config
command line argument or the RADICALE_CONFIG
environment variable. Multiple configuration files can be separated by :
(resp. ;
on Windows). Paths that start with ?
are optional.
The same example configuration via command line arguments looks like:
-python3 -m radicale --server-hosts 0.0.0.0:5232,[::]:5232 --auth-type htpasswd --htpasswd-filename ~/.config/radicale/users --htpasswd-encryption md5
python3 -m radicale --server-hosts 0.0.0.0:5232,[::]:5232 --auth-type htpasswd --htpasswd-filename ~/.config/radicale/users --htpasswd-encryption md5
Add the argument --config ""
to stop Radicale from loading the default configuration files. Run python3 -m radicale --help
for more information.
In the following, all configuration categories and options are described.
Available methods:
plain
: Passwords are stored in plaintext. This is obviously not secure! The htpasswd file for this can be created by hand and looks like:
user1:password1
-user2:password2
-
+user2:password2
bcrypt
: This uses a modified version of the Blowfish stream cipher. It's very secure. The installation of radicale[bcrypt] is required for this.
md5
: This uses an iterated md5 digest of the password with a salt.
Default: md5
In this section additional HTTP headers that are sent to clients can be specified.
An example to relax the same-origin policy:
- +Access-Control-Allow-Origin = *
Many clients do not support the creation of new calendars and address books. You can use Radicale's web interface (e.g. http://localhost:5232) to create and manage address books and calendars.
+Many clients do not support the creation of new calendars and address books. You can use Radicale's web interface (e.g. http://localhost:5232) to create and manage address books and calendars.
In some clients you can just enter the URL of the Radicale server (e.g. http://localhost:5232
) and your user name. In others, you have to enter the URL of the collection directly (e.g. http://localhost:5232/user/calendar
).
This is not the recommended way of creating and managing your calendars and address books. Use Radicale's web interface or a client with support for it (e.g. DAVx⁵).
To create a new calendar run something like:
-$ curl -u user -X MKCOL 'http://localhost:5232/user/calendar' --data \
-'<?xml version="1.0" encoding="UTF-8" ?>
-<create xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:I="http://apple.com/ns/ical/">
- <set>
- <prop>
- <resourcetype>
- <collection />
- <C:calendar />
- </resourcetype>
- <C:supported-calendar-component-set>
- <C:comp name="VEVENT" />
- <C:comp name="VJOURNAL" />
- <C:comp name="VTODO" />
- </C:supported-calendar-component-set>
- <displayname>Calendar</displayname>
- <C:calendar-description>Example calendar</C:calendar-description>
- <I:calendar-color>#ff0000ff</I:calendar-color>
- </prop>
- </set>
-</create>'
$ curl -u user -X MKCOL 'http://localhost:5232/user/calendar' --data \
+'<?xml version="1.0" encoding="UTF-8" ?>
+<create xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:I="http://apple.com/ns/ical/">
+ <set>
+ <prop>
+ <resourcetype>
+ <collection />
+ <C:calendar />
+ </resourcetype>
+ <C:supported-calendar-component-set>
+ <C:comp name="VEVENT" />
+ <C:comp name="VJOURNAL" />
+ <C:comp name="VTODO" />
+ </C:supported-calendar-component-set>
+ <displayname>Calendar</displayname>
+ <C:calendar-description>Example calendar</C:calendar-description>
+ <I:calendar-color>#ff0000ff</I:calendar-color>
+ </prop>
+ </set>
+</create>'
To create a new address book run something like:
-$ curl -u user -X MKCOL 'http://localhost:5232/user/addressbook' --data \
-'<?xml version="1.0" encoding="UTF-8" ?>
-<create xmlns="DAV:" xmlns:CR="urn:ietf:params:xml:ns:carddav">
- <set>
- <prop>
- <resourcetype>
- <collection />
- <CR:addressbook />
- </resourcetype>
- <displayname>Address book</displayname>
- <CR:addressbook-description>Example address book</CR:addressbook-description>
- </prop>
- </set>
-</create>'
$ curl -u user -X MKCOL 'http://localhost:5232/user/addressbook' --data \
+'<?xml version="1.0" encoding="UTF-8" ?>
+<create xmlns="DAV:" xmlns:CR="urn:ietf:params:xml:ns:carddav">
+ <set>
+ <prop>
+ <resourcetype>
+ <collection />
+ <CR:addressbook />
+ </resourcetype>
+ <displayname>Address book</displayname>
+ <CR:addressbook-description>Example address book</CR:addressbook-description>
+ </prop>
+ </set>
+</create>'
The collection /USERNAME
will be created automatically, when the user authenticates to Radicale for the first time. Clients with automatic discovery of collections will only show calendars and address books that are direct children of the path /USERNAME/
.
Delete the collections by running something like:
- +$ curl -u user -X DELETE 'http://localhost:5232/user/calendar'
This section describes the format of the rights file for the from_file
authentication backend. The configuration option file
in the rights
section must point to the rights file.
The recommended rights method is owner_only
. If access to calendars and address books outside the home directory of users (that's /USERNAME/
) is granted, clients won't detect these collections and will not show them to the user. This is only useful if you access calendars and address books directly via URL.
An example rights file:
-# Allow reading root collection for authenticated users
-[root]
-user: .+
-collection:
-permissions: R
-
-# Allow reading and writing principal collection (same as user name)
-[principal]
-user: .+
-collection: {user}
-permissions: RW
-
-# Allow reading and writing calendars and address books that are direct
-# children of the principal collection
-[calendars]
-user: .+
-collection: {user}/[^/]+
-permissions: rw
# Allow reading root collection for authenticated users
+[root]
+user: .+
+collection:
+permissions: R
+
+# Allow reading and writing principal collection (same as user name)
+[principal]
+user: .+
+collection: {user}
+permissions: RW
+
+# Allow reading and writing calendars and address books that are direct
+# children of the principal collection
+[calendars]
+user: .+
+collection: {user}/[^/]+
+permissions: rw
The titles of the sections are ignored (but must be unique). The keys user
and collection
contain regular expressions, that are matched against the user name and the path of the collection. Permissions from the first matching section are used. If no section matches, access gets denied.
The user name is empty for anonymous users. Therefore, the regex .+
only matches authenticated users and .*
matches everyone (including anonymous users).
The path of the collection is separated by /
and has no leading or trailing /
. Therefore, the path of the root collection is empty.
Use the flock utility.
-# Exclusive
-$ flock --exclusive /path/to/storage/.Radicale.lock COMMAND
-# Shared
-$ flock --shared /path/to/storage/.Radicale.lock COMMAND
# Exclusive
+$ flock --exclusive /path/to/storage/.Radicale.lock COMMAND
+# Shared
+$ flock --shared /path/to/storage/.Radicale.lock COMMAND
To create a new collection, you have to create the corresponding folder in the file system storage (e.g. collection-root/user/calendar
). To tell Radicale and clients that the collection is a calendar, you have to create the file .Radicale.props
with the following content in the folder:
{"tag": "VCALENDAR"}
The calendar is now available at the URL path /user/calendar
. For address books the file must contain:
{"tag": "VADDRESSBOOK"}
Calendar and address book collections must not have any child collections. Clients with automatic discovery of collections will only show calendars and address books that are direct children of the path /USERNAME/
.
Delete collections by deleting the corresponding folders.
To get started we walk through the creation of a simple authentication plugin, that accepts login attempts with a static password.
The easiest way to develop and install python modules is Distutils. For a minimal setup create the file setup.py
with the following content in an empty folder:
#!/usr/bin/env python3
-
-from distutils.core import setup
-
-setup(name="radicale_static_password_auth",
- packages=["radicale_static_password_auth"])
#!/usr/bin/env python3
+
+from distutils.core import setup
+
+="radicale_static_password_auth",
+ setup(name=["radicale_static_password_auth"]) packages
In the same folder create the sub-folder radicale_static_password_auth
. The folder must have the same name as specified in packages
above.
Create the file __init__.py
in the radicale_static_password_auth
folder with the following content:
from radicale.auth import BaseAuth
-from radicale.log import logger
-
-PLUGIN_CONFIG_SCHEMA = {"auth": {
- "password": {"value": "", "type": str}}}
-
-
-class Auth(BaseAuth):
- def __init__(self, configuration):
- super().__init__(configuration.copy(PLUGIN_CONFIG_SCHEMA))
-
- def login(self, login, password):
- # Get password from configuration option
- static_password = self.configuration.get("auth", "password")
- # Check authentication
- logger.info("Login attempt by %r with password %r",
- login, password)
- if password == static_password:
- return login
- return ""
from radicale.auth import BaseAuth
+from radicale.log import logger
+
+= {"auth": {
+ PLUGIN_CONFIG_SCHEMA "password": {"value": "", "type": str}}}
+
+
+class Auth(BaseAuth):
+def __init__(self, configuration):
+ super().__init__(configuration.copy(PLUGIN_CONFIG_SCHEMA))
+
+def login(self, login, password):
+ # Get password from configuration option
+ = self.configuration.get("auth", "password")
+ static_password # Check authentication
+ "Login attempt by %r with password %r",
+ logger.info(
+ login, password)if password == static_password:
+ return login
+ return ""
Install the python module by running the following command in the same folder as setup.py
:
python3 -m pip install .
To make use this great creation in Radicale, set the configuration option type
in the auth
section to radicale_static_password_auth
:
[auth]
+type = radicale_static_password_auth
+password = secret
You can uninstall the module with:
- +python3 -m pip uninstall radicale_static_password_auth
Radicale is available on PyPI. To install, just type as superuser:
-$ python3 -m pip install --upgrade radicale
-
+$ python3 -m pip install --upgrade radicale
If you want the development version of Radicale, take a look at the git repository on GitHub, or install it directly with:
-$ python3 -m pip install --upgrade https://github.com/Kozea/Radicale/archive/master.tar.gz
-
+$ python3 -m pip install --upgrade https://github.com/Kozea/Radicale/archive/master.tar.gz
You can also download the content of the repository as an archive.
Radicale is really easy to install and works out-of-the-box.
-$ python3 -m pip install --upgrade https://github.com/Kozea/Radicale/archive/master.tar.gz
-$ python3 -m radicale --storage-filesystem-folder=~/.var/lib/radicale/collections
When the server is launched, open http://localhost:5232/ in your browser! You can login with any username and password.
+$ python3 -m pip install --upgrade https://github.com/Kozea/Radicale/archive/master.tar.gz
+$ python3 -m radicale --storage-filesystem-folder=~/.var/lib/radicale/collections
When the server is launched, open http://localhost:5232/ in your browser! You can login with any username and password.
Want more? Check the tutorials and the documentation.
First, make sure that python 3.5 or later (python ≥ 3.6 is recommended) and pip are installed. On most distributions it should be enough to install the package python3-pip
.
Then open a console and type:
-# Run the following command as root or
-# add the --user argument to only install for the current user
-$ python3 -m pip install --upgrade https://github.com/Kozea/Radicale/archive/master.tar.gz
-$ python3 -m radicale --storage-filesystem-folder=~/.var/lib/radicale/collections
Victory! Open http://localhost:5232/ in your browser! You can log in with any username and password.
+# Run the following command as root or
+# add the --user argument to only install for the current user
+$ python3 -m pip install --upgrade https://github.com/Kozea/Radicale/archive/master.tar.gz
+$ python3 -m radicale --storage-filesystem-folder=~/.var/lib/radicale/collections
Victory! Open http://localhost:5232/ in your browser! You can log in with any username and password.
The first step is to install Python. Go to python.org and download the latest version of Python 3. Then run the installer. On the first window of the installer, check the "Add Python to PATH" box and click on "Install now". Wait a couple of minutes, it's done!
Launch a command prompt and type:
-C:\Users\User> python -m pip install --upgrade https://github.com/Kozea/Radicale/archive/master.tar.gz
-C:\Users\User> python -m radicale --storage-filesystem-folder=~/radicale/collections
Victory! Open http://localhost:5232/ in your browser! You can log in with any username and password.
+:\Users\User> python -m pip install --upgrade https://github.com/Kozea/Radicale/archive/master.tar.gz
+ C:\Users\User> python -m radicale --storage-filesystem-folder=~/radicale/collections C
Victory! Open http://localhost:5232/ in your browser! You can log in with any username and password.
The users
file can be created and managed with htpasswd:
# Create a new htpasswd file with the user "user1"
-$ htpasswd -c /path/to/users user1
-New password:
-Re-type new password:
-# Add another user
-$ htpasswd /path/to/users user2
-New password:
-Re-type new password:
# Create a new htpasswd file with the user "user1"
+$ htpasswd -c /path/to/users user1
+New password:
+Re-type new password:
+# Add another user
+$ htpasswd /path/to/users user2
+New password:
+Re-type new password:
Authentication can be enabled with the following configuration:
-[auth]
-type = htpasswd
-htpasswd_filename = /path/to/users
-# encryption method used in the htpasswd file
-htpasswd_encryption = md5
[auth]
+type = htpasswd
+htpasswd_filename = /path/to/users
+# encryption method used in the htpasswd file
+htpasswd_encryption = md5
Create the users
file by hand with lines containing the user name and password separated by :
. Example:
user1:password1
-user2:password2
-
+user2:password2
Authentication can be enabled with the following configuration:
-[auth]
-type = htpasswd
-htpasswd_filename = /path/to/users
-# encryption method used in the htpasswd file
-htpasswd_encryption = plain
[auth]
+type = htpasswd
+htpasswd_filename = /path/to/users
+# encryption method used in the htpasswd file
+htpasswd_encryption = plain
The default configuration binds the server to localhost. It can't be reached from other computers. This can be changed with the following configuration options (IPv4 and IPv6):
- +[server]
+hosts = 0.0.0.0:5232, [::]:5232
Data is stored in the folder /var/lib/radicale/collections
. The path can be changed with the following configuration:
[storage]
+filesystem_folder = /path/to/storage
@@ -340,16 +472,16 @@ user2:password2Security: The storage folder should not be readable by unauthorized users. Otherwise, they can read the calendar data and lock the storage. You can find OS dependent instructions in the Running as a service section.
Radicale enforces limits on the maximum number of parallel connections, the maximum file size (important for contacts with big photos) and the rate of incorrect authentication attempts. Connections are terminated after a timeout. The default values should be fine for most scenarios.
-[server]
-max_connections = 20
-# 100 Megabyte
-max_content_length = 100000000
-# 30 seconds
-timeout = 30
-
-[auth]
-# Average delay after failed login attempts in seconds
-delay = 1
[server]
+max_connections = 20
+# 100 Megabyte
+max_content_length = 100000000
+# 30 seconds
+timeout = 30
+
+[auth]
+# Average delay after failed login attempts in seconds
+delay = 1
Security: The storage should not be readable by others. (Run chmod -R o= /var/lib/radicale/collections
as root.)
Create the file /etc/systemd/system/radicale.service
:
[Unit]
-Description=A simple CalDAV (calendar) and CardDAV (contact) server
-After=network.target
-Requires=network.target
-
-[Service]
-ExecStart=/usr/bin/env python3 -m radicale
-Restart=on-failure
-User=radicale
-# Deny other users access to the calendar data
-UMask=0027
-# Optional security settings
-PrivateTmp=true
-ProtectSystem=strict
-ProtectHome=true
-PrivateDevices=true
-ProtectKernelTunables=true
-ProtectKernelModules=true
-ProtectControlGroups=true
-NoNewPrivileges=true
-ReadWritePaths=/var/lib/radicale/collections
-
-[Install]
-WantedBy=multi-user.target
[Unit]
+Description=A simple CalDAV (calendar) and CardDAV (contact) server
+After=network.target
+Requires=network.target
+
+[Service]
+ExecStart=/usr/bin/env python3 -m radicale
+Restart=on-failure
+User=radicale
+# Deny other users access to the calendar data
+UMask=0027
+# Optional security settings
+PrivateTmp=true
+ProtectSystem=strict
+ProtectHome=true
+PrivateDevices=true
+ProtectKernelTunables=true
+ProtectKernelModules=true
+ProtectControlGroups=true
+NoNewPrivileges=true
+ReadWritePaths=/var/lib/radicale/collections
+
+[Install]
+WantedBy=multi-user.target
Radicale will load the configuration file from /etc/radicale/config
.
To enable and manage the service run:
-# Enable the service
-$ systemctl enable radicale
-# Start the service
-$ systemctl start radicale
-# Check the status of the service
-$ systemctl status radicale
-# View all log messages
-$ journalctl --unit radicale.service
# Enable the service
+$ systemctl enable radicale
+# Start the service
+$ systemctl start radicale
+# Check the status of the service
+$ systemctl status radicale
+# View all log messages
+$ journalctl --unit radicale.service
Create the file ~/.config/systemd/user/radicale.service
:
[Unit]
-Description=A simple CalDAV (calendar) and CardDAV (contact) server
-
-[Service]
-ExecStart=/usr/bin/env python3 -m radicale
-Restart=on-failure
-
-[Install]
-WantedBy=default.target
[Unit]
+Description=A simple CalDAV (calendar) and CardDAV (contact) server
+
+[Service]
+ExecStart=/usr/bin/env python3 -m radicale
+Restart=on-failure
+
+[Install]
+WantedBy=default.target
Radicale will load the configuration file from ~/.config/radicale/config
. You should set the configuration option filesystem_folder
in the storage
section to something like ~/.var/lib/radicale/collections
.
To enable and manage the service run:
-# Enable the service
-$ systemctl --user enable radicale
-# Start the service
-$ systemctl --user start radicale
-# Check the status of the service
-$ systemctl --user status radicale
-# View all log messages
-$ journalctl --user --unit radicale.service
# Enable the service
+$ systemctl --user enable radicale
+# Start the service
+$ systemctl --user start radicale
+# Check the status of the service
+$ systemctl --user status radicale
+# View all log messages
+$ journalctl --user --unit radicale.service
Example Apache configuration:
-RewriteEngine On
-RewriteRule ^/radicale$ /radicale/ [R,L]
-
-<Location "/radicale/">
- ProxyPass http://localhost:5232/ retry=0
- ProxyPassReverse http://localhost:5232/
- RequestHeader set X-Script-Name /radicale
-</Location>
RewriteEngine On
+ ^/radicale$ /radicale/ [R,L]
+ RewriteRule
+<Location "/radicale/">
+ http://localhost:5232/ retry=0
+ ProxyPass http://localhost:5232/
+ ProxyPassReverse set X-Script-Name /radicale
+ RequestHeader</Location>
Example Apache .htaccess configuration:
-DirectoryIndex disabled
-RewriteEngine On
-RewriteRule ^(.*)$ http://localhost:5232/$1 [P,L]
-
-# Set to directory of .htaccess file:
-RequestHeader set X-Script-Name /radicale
disabled
+ DirectoryIndexRewriteEngine On
+ ^(.*)$ http://localhost:5232/$1 [P,L]
+ RewriteRule
+# Set to directory of .htaccess file:
+ set X-Script-Name /radicale RequestHeader
Be reminded that Radicale's default configuration enforces limits on the maximum number of parallel connections, the maximum file size and the rate of incorrect authentication attempts. Connections are terminated after a timeout.
Example Apache configuration:
-RewriteEngine On
-RewriteRule ^/radicale$ /radicale/ [R,L]
-
-<Location "/radicale/">
- AuthType Basic
- AuthName "Radicale - Password Required"
- AuthUserFile "/etc/radicale/htpasswd"
- Require valid-user
-
- ProxyPass http://localhost:5232/ retry=0
- ProxyPassReverse http://localhost:5232/
- RequestHeader set X-Script-Name /radicale
- RequestHeader set X-Remote-User expr=%{REMOTE_USER}
-</Location>
RewriteEngine On
+ ^/radicale$ /radicale/ [R,L]
+ RewriteRule
+<Location "/radicale/">
+AuthType Basic
+ "Radicale - Password Required"
+ AuthName "/etc/radicale/htpasswd"
+ AuthUserFile valid-user
+ Require
+ http://localhost:5232/ retry=0
+ ProxyPass http://localhost:5232/
+ ProxyPassReverse set X-Script-Name /radicale
+ RequestHeader set X-Remote-User expr=%{REMOTE_USER}
+ RequestHeader</Location>
Example Apache .htaccess configuration:
-DirectoryIndex disabled
-RewriteEngine On
-RewriteRule ^(.*)$ http://localhost:5232/$1 [P,L]
-
-AuthType Basic
-AuthName "Radicale - Password Required"
-AuthUserFile "/etc/radicale/htpasswd"
-Require valid-user
-
-# Set to directory of .htaccess file:
-RequestHeader set X-Script-Name /radicale
-RequestHeader set X-Remote-User expr=%{REMOTE_USER}
disabled
+ DirectoryIndexRewriteEngine On
+ ^(.*)$ http://localhost:5232/$1 [P,L]
+ RewriteRule
+AuthType Basic
+ "Radicale - Password Required"
+ AuthName "/etc/radicale/htpasswd"
+ AuthUserFile valid-user
+ Require
+# Set to directory of .htaccess file:
+ set X-Script-Name /radicale
+ RequestHeader set X-Remote-User expr=%{REMOTE_USER} RequestHeader
@@ -520,14 +650,14 @@ user2:password2Security: Untrusted clients should not be able to access the Radicale server directly. Otherwise, they can authenticate as any user.
SSL certificates can be used to encrypt and authenticate the connection between Radicale and the reverse proxy. First you have to generate a certificate for Radicale and a certificate for the reverse proxy. The following commands generate self-signed certificates. You will be asked to enter additional information about the certificate, the values don't matter and you can keep the defaults.
-$ openssl req -x509 -newkey rsa:4096 -keyout server_key.pem -out server_cert.pem -nodes -days 9999
-$ openssl req -x509 -newkey rsa:4096 -keyout client_key.pem -out client_cert.pem -nodes -days 9999
$ openssl req -x509 -newkey rsa:4096 -keyout server_key.pem -out server_cert.pem -nodes -days 9999
+$ openssl req -x509 -newkey rsa:4096 -keyout client_key.pem -out client_cert.pem -nodes -days 9999
Use the following configuration for Radicale:
-[server]
-ssl = True
-certificate = /path/to/server_cert.pem
-key = /path/to/server_key.pem
-certificate_authority = /path/to/client_cert.pem
[server]
+ssl = True
+certificate = /path/to/server_cert.pem
+key = /path/to/server_key.pem
+certificate_authority = /path/to/client_cert.pem
Example nginx configuration:
location /radicale/ {
proxy_pass https://localhost:5232/;
@@ -536,8 +666,7 @@ user2:password2
proxy_ssl_certificate /path/to/client_cert.pem;
proxy_ssl_certificate_key /path/to/client_key.pem;
proxy_ssl_trusted_certificate /path/to/server_cert.pem;
-}
-
+}
Radicale is compatible with the WSGI specification.
A configuration file can be set with the RADICALE_CONFIG
environment variable, otherwise no configuration file is loaded and the default configuration is used.
Example uWSGI configuration:
-[uwsgi]
-http-socket = 127.0.0.1:5232
-processes = 8
-plugin = python3
-module = radicale
-env = RADICALE_CONFIG=/etc/radicale/config
[uwsgi]
+http-socket = 127.0.0.1:5232
+processes = 8
+plugin = python3
+module = radicale
+env = RADICALE_CONFIG=/etc/radicale/config
Example Gunicorn configuration:
- +gunicorn --bind '127.0.0.1:5232' --workers 8 --env 'RADICALE_CONFIG=/etc/radicale/config' radicale
Set the configuration option type
in the auth
section to remote_user
. Radicale uses the user name provided by the WSGI server and disables authentication over HTTP.
The repository must be initialized by running git init
in the file system folder. Internal files of Radicale can be excluded by creating the file .gitignore
with the following content:
.Radicale.cache
.Radicale.lock
-.Radicale.tmp-*
-
+.Radicale.tmp-*
The configuration option hook
in the storage
section must be set to the following command:
git add -A && (git diff --cached --quiet || git commit -m "Changes by "%(user)s)
The command gets executed after every change to the storage and commits the changes into the git repository.
Radicale can be configured with a configuration file or with command line arguments.
An example configuration file looks like:
-[server]
-# Bind all addresses
-hosts = 0.0.0.0:5232, [::]:5232
-
-[auth]
-type = htpasswd
-htpasswd_filename = ~/.config/radicale/users
-htpasswd_encryption = md5
-
-[storage]
-filesystem_folder = ~/.var/lib/radicale/collections
[server]
+# Bind all addresses
+hosts = 0.0.0.0:5232, [::]:5232
+
+[auth]
+type = htpasswd
+htpasswd_filename = ~/.config/radicale/users
+htpasswd_encryption = md5
+
+[storage]
+filesystem_folder = ~/.var/lib/radicale/collections
Radicale tries to load configuration files from /etc/radicale/config
and ~/.config/radicale/config
. Custom paths can be specified with the --config /path/to/config
command line argument or the RADICALE_CONFIG
environment variable. Multiple configuration files can be separated by :
(resp. ;
on Windows). Paths that start with ?
are optional.
The same example configuration via command line arguments looks like:
-python3 -m radicale --server-hosts 0.0.0.0:5232,[::]:5232 --auth-type htpasswd --auth-htpasswd-filename ~/.config/radicale/users --auth-htpasswd-encryption md5
python3 -m radicale --server-hosts 0.0.0.0:5232,[::]:5232 --auth-type htpasswd --auth-htpasswd-filename ~/.config/radicale/users --auth-htpasswd-encryption md5
Add the argument --config ""
to stop Radicale from loading the default configuration files. Run python3 -m radicale --help
for more information.
In the following, all configuration categories and options are described.
Available methods:
plain
: Passwords are stored in plaintext. This is obviously not secure! The htpasswd file for this can be created by hand and looks like:
user1:password1
-user2:password2
-
+user2:password2
bcrypt
: This uses a modified version of the Blowfish stream cipher. It's very secure. The installation of radicale[bcrypt] is required for this.
md5
: This uses an iterated md5 digest of the password with a salt.
Default: md5
In this section additional HTTP headers that are sent to clients can be specified.
An example to relax the same-origin policy:
- +Access-Control-Allow-Origin = *
Many clients do not support the creation of new calendars and address books. You can use Radicale's web interface (e.g. http://localhost:5232) to create and manage address books and calendars.
+Many clients do not support the creation of new calendars and address books. You can use Radicale's web interface (e.g. http://localhost:5232) to create and manage address books and calendars.
In some clients you can just enter the URL of the Radicale server (e.g. http://localhost:5232
) and your user name. In others, you have to enter the URL of the collection directly (e.g. http://localhost:5232/user/calendar
).
This is not the recommended way of creating and managing your calendars and address books. Use Radicale's web interface or a client with support for it (e.g. DAVx⁵).
To create a new calendar run something like:
-$ curl -u user -X MKCOL 'http://localhost:5232/user/calendar' --data \
-'<?xml version="1.0" encoding="UTF-8" ?>
-<create xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:I="http://apple.com/ns/ical/">
- <set>
- <prop>
- <resourcetype>
- <collection />
- <C:calendar />
- </resourcetype>
- <C:supported-calendar-component-set>
- <C:comp name="VEVENT" />
- <C:comp name="VJOURNAL" />
- <C:comp name="VTODO" />
- </C:supported-calendar-component-set>
- <displayname>Calendar</displayname>
- <C:calendar-description>Example calendar</C:calendar-description>
- <I:calendar-color>#ff0000ff</I:calendar-color>
- </prop>
- </set>
-</create>'
$ curl -u user -X MKCOL 'http://localhost:5232/user/calendar' --data \
+'<?xml version="1.0" encoding="UTF-8" ?>
+<create xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:I="http://apple.com/ns/ical/">
+ <set>
+ <prop>
+ <resourcetype>
+ <collection />
+ <C:calendar />
+ </resourcetype>
+ <C:supported-calendar-component-set>
+ <C:comp name="VEVENT" />
+ <C:comp name="VJOURNAL" />
+ <C:comp name="VTODO" />
+ </C:supported-calendar-component-set>
+ <displayname>Calendar</displayname>
+ <C:calendar-description>Example calendar</C:calendar-description>
+ <I:calendar-color>#ff0000ff</I:calendar-color>
+ </prop>
+ </set>
+</create>'
To create a new address book run something like:
-$ curl -u user -X MKCOL 'http://localhost:5232/user/addressbook' --data \
-'<?xml version="1.0" encoding="UTF-8" ?>
-<create xmlns="DAV:" xmlns:CR="urn:ietf:params:xml:ns:carddav">
- <set>
- <prop>
- <resourcetype>
- <collection />
- <CR:addressbook />
- </resourcetype>
- <displayname>Address book</displayname>
- <CR:addressbook-description>Example address book</CR:addressbook-description>
- </prop>
- </set>
-</create>'
$ curl -u user -X MKCOL 'http://localhost:5232/user/addressbook' --data \
+'<?xml version="1.0" encoding="UTF-8" ?>
+<create xmlns="DAV:" xmlns:CR="urn:ietf:params:xml:ns:carddav">
+ <set>
+ <prop>
+ <resourcetype>
+ <collection />
+ <CR:addressbook />
+ </resourcetype>
+ <displayname>Address book</displayname>
+ <CR:addressbook-description>Example address book</CR:addressbook-description>
+ </prop>
+ </set>
+</create>'
The collection /USERNAME
will be created automatically, when the user authenticates to Radicale for the first time. Clients with automatic discovery of collections will only show calendars and address books that are direct children of the path /USERNAME/
.
Delete the collections by running something like:
- +$ curl -u user -X DELETE 'http://localhost:5232/user/calendar'
This section describes the format of the rights file for the from_file
authentication backend. The configuration option file
in the rights
section must point to the rights file.
The recommended rights method is owner_only
. If access to calendars and address books outside the home directory of users (that's /USERNAME/
) is granted, clients won't detect these collections and will not show them to the user. This is only useful if you access calendars and address books directly via URL.
An example rights file:
-# Allow reading root collection for authenticated users
-[root]
-user: .+
-collection:
-permissions: R
-
-# Allow reading and writing principal collection (same as user name)
-[principal]
-user: .+
-collection: {user}
-permissions: RW
-
-# Allow reading and writing calendars and address books that are direct
-# children of the principal collection
-[calendars]
-user: .+
-collection: {user}/[^/]+
-permissions: rw
# Allow reading root collection for authenticated users
+[root]
+user: .+
+collection:
+permissions: R
+
+# Allow reading and writing principal collection (same as user name)
+[principal]
+user: .+
+collection: {user}
+permissions: RW
+
+# Allow reading and writing calendars and address books that are direct
+# children of the principal collection
+[calendars]
+user: .+
+collection: {user}/[^/]+
+permissions: rw
The titles of the sections are ignored (but must be unique). The keys user
and collection
contain regular expressions, that are matched against the user name and the path of the collection. Permissions from the first matching section are used. If no section matches, access gets denied.
The user name is empty for anonymous users. Therefore, the regex .+
only matches authenticated users and .*
matches everyone (including anonymous users).
The path of the collection is separated by /
and has no leading or trailing /
. Therefore, the path of the root collection is empty.
Use the flock utility.
-# Exclusive
-$ flock --exclusive /path/to/storage/.Radicale.lock COMMAND
-# Shared
-$ flock --shared /path/to/storage/.Radicale.lock COMMAND
# Exclusive
+$ flock --exclusive /path/to/storage/.Radicale.lock COMMAND
+# Shared
+$ flock --shared /path/to/storage/.Radicale.lock COMMAND
To create a new collection, you have to create the corresponding folder in the file system storage (e.g. collection-root/user/calendar
). To tell Radicale and clients that the collection is a calendar, you have to create the file .Radicale.props
with the following content in the folder:
{"tag": "VCALENDAR"}
The calendar is now available at the URL path /user/calendar
. For address books the file must contain:
{"tag": "VADDRESSBOOK"}
Calendar and address book collections must not have any child collections. Clients with automatic discovery of collections will only show calendars and address books that are direct children of the path /USERNAME/
.
Delete collections by deleting the corresponding folders.
To get started we walk through the creation of a simple authentication plugin, that accepts login attempts with a static password.
The easiest way to develop and install python modules is Distutils. For a minimal setup create the file setup.py
with the following content in an empty folder:
#!/usr/bin/env python3
-
-from distutils.core import setup
-
-setup(name="radicale_static_password_auth",
- packages=["radicale_static_password_auth"])
#!/usr/bin/env python3
+
+from distutils.core import setup
+
+="radicale_static_password_auth",
+ setup(name=["radicale_static_password_auth"]) packages
In the same folder create the sub-folder radicale_static_password_auth
. The folder must have the same name as specified in packages
above.
Create the file __init__.py
in the radicale_static_password_auth
folder with the following content:
from radicale.auth import BaseAuth
-from radicale.log import logger
-
-PLUGIN_CONFIG_SCHEMA = {"auth": {
- "password": {"value": "", "type": str}}}
-
-
-class Auth(BaseAuth):
- def __init__(self, configuration):
- super().__init__(configuration.copy(PLUGIN_CONFIG_SCHEMA))
-
- def login(self, login, password):
- # Get password from configuration option
- static_password = self.configuration.get("auth", "password")
- # Check authentication
- logger.info("Login attempt by %r with password %r",
- login, password)
- if password == static_password:
- return login
- return ""
from radicale.auth import BaseAuth
+from radicale.log import logger
+
+= {"auth": {
+ PLUGIN_CONFIG_SCHEMA "password": {"value": "", "type": str}}}
+
+
+class Auth(BaseAuth):
+def __init__(self, configuration):
+ super().__init__(configuration.copy(PLUGIN_CONFIG_SCHEMA))
+
+def login(self, login, password):
+ # Get password from configuration option
+ = self.configuration.get("auth", "password")
+ static_password # Check authentication
+ "Login attempt by %r with password %r",
+ logger.info(
+ login, password)if password == static_password:
+ return login
+ return ""
Install the python module by running the following command in the same folder as setup.py
:
python3 -m pip install .
To make use this great creation in Radicale, set the configuration option type
in the auth
section to radicale_static_password_auth
:
[auth]
+type = radicale_static_password_auth
+password = secret
You can uninstall the module with:
- +python3 -m pip uninstall radicale_static_password_auth
Radicale is available on PyPI. To install, just type as superuser:
-$ python3 -m pip install --upgrade radicale
-
+$ python3 -m pip install --upgrade radicale
If you want the development version of Radicale, take a look at the git repository on GitHub, or install it directly with:
-$ python3 -m pip install --upgrade https://github.com/Kozea/Radicale/archive/master.tar.gz
-
+$ python3 -m pip install --upgrade https://github.com/Kozea/Radicale/archive/master.tar.gz
You can also download the content of the repository as an archive.