diff --git a/scripttease/data/inventory/radicale/meta.ini b/scripttease/data/inventory/radicale/meta.ini
new file mode 100644
index 0000000..ee4d46d
--- /dev/null
+++ b/scripttease/data/inventory/radicale/meta.ini
@@ -0,0 +1,6 @@
+[package]
+description = Radicale is a CalDAV and CardDAV server. These steps install Radicale as a system-wide service, with an Apache reverse proxy.
+docs = https://radicale.org
+tags = CalDAV, CardDav
+title = Radicale
+version = 0.1.0-d
diff --git a/scripttease/data/inventory/radicale/steps.ini b/scripttease/data/inventory/radicale/steps.ini
new file mode 100644
index 0000000..d78ddb6
--- /dev/null
+++ b/scripttease/data/inventory/radicale/steps.ini
@@ -0,0 +1,49 @@
+[make sure a maintenance root exists]
+mkdir: /var/www/maint/www
+group: www-data
+owner: www-data
+recursive: yes
+
+[install radicale]
+pip3: radicale
+
+[create radicale configuration directory]
+mkdir: /etc/radicale/config
+owner: radicale
+recursive: yes
+
+[create the radicale configuration file]
+template: config.ini /etc/radicale/config/config.ini
+
+[create the radicale user]
+user.add: radicale
+home: /
+login: /sbin/nologin
+system: yes
+; useradd --system --user-group --home-dir / --shell /sbin/nologin radicale
+
+[create the systemd service file for radicale]
+template: radicale.service /etc/systemd/system/radicale.service
+
+[start the radicale service]
+start: radicale
+
+[create the initial apache config file]
+template: httpd.conf /etc/apache2/sites-available/{{ domain_name }}.conf
+
+[enable the site]
+apache.enable: {{ domain_name }}
+
+[reload apache]
+apache.reload:
+
+[get an SSL cert]
+ssl: {{ domain_name }}
+email: {{ webmaster_email }}
+
+[create the SSL version of the apache config file]
+template: httpd.conf /etc/apache2/sites-available/{{ domain_name }}.conf
+ssl_enabled: yes
+
+[restart apache]
+apache.restart:
diff --git a/scripttease/data/inventory/radicale/templates/config.ini b/scripttease/data/inventory/radicale/templates/config.ini
new file mode 100644
index 0000000..2e4a1b9
--- /dev/null
+++ b/scripttease/data/inventory/radicale/templates/config.ini
@@ -0,0 +1,16 @@
+[auth]
+type = htpasswd
+htpasswd_filename = {{ config_path}}/users.htpasswd
+htpasswd_encryption = md5
+;delay = 1
+
+;[server]
+;hosts = 0.0.0.0:5232, [::]:5232
+;max_connections = 20
+; 100 Megabyte
+;max_content_length = 100000000
+; 30 seconds
+;timeout = 30
+
+;[storage]
+;filesystem_folder = {{ data_path }}
diff --git a/scripttease/data/inventory/radicale/templates/httpd.conf b/scripttease/data/inventory/radicale/templates/httpd.conf
new file mode 100644
index 0000000..e97ca22
--- /dev/null
+++ b/scripttease/data/inventory/radicale/templates/httpd.conf
@@ -0,0 +1,28 @@
+# The port 80 host is required for renewing Let's Encrypt certificates.
+
+ ServerName {{ domain_name }}
+ ServerAlias *.{{ domain_name }}
+ RewriteEngine On
+ RewriteCond %{HTTPS} off
+ RewriteCond %{REQUEST_URI} !^/.well-known [NC]
+ RewriteRule (.*) https://%{SERVER_NAME}/$1 [R,L]
+ DocumentRoot /var/www/maint/www
+
+
+{% if ssl_enabled %}
+# The 443 host is where the project is actually served.
+
+ ServerName {{ domain_name }}
+ DocumentRoot /var/www/maint/www
+
+ SSLEngine on
+ SSLCertificateKeyFile /etc/letsencrypt/live/{{ domain_name }}/privkey.pem
+ SSLCertificateFile /etc/letsencrypt/live/{{ domain_name }}/fullchain.pem
+
+
+ ProxyPass http://localhost:5232/ retry=0
+ ProxyPassReverse http://localhost:5232/
+ RequestHeader set X-Script-Name /radicale
+
+
+{% endif %}
\ No newline at end of file
diff --git a/scripttease/data/inventory/radicale/templates/radicale.service b/scripttease/data/inventory/radicale/templates/radicale.service
new file mode 100644
index 0000000..fbacb6a
--- /dev/null
+++ b/scripttease/data/inventory/radicale/templates/radicale.service
@@ -0,0 +1,24 @@
+[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={{ data_path }}
+
+[Install]
+WantedBy=multi-user.target
diff --git a/scripttease/data/inventory/radicale/variables.ini b/scripttease/data/inventory/radicale/variables.ini
new file mode 100644
index 0000000..848e46c
--- /dev/null
+++ b/scripttease/data/inventory/radicale/variables.ini
@@ -0,0 +1,19 @@
+[domain_name]
+comment = The domain name to use for the Radicale host.
+value = cal.example.com
+
+[radicale_uri]
+comment = The partial URI where Radicale is available. Include the trailing slash.
+value = /
+
+[config_path]
+comment = The path to configuration files.
+value = /etc/radicale/config
+
+[data_path]
+comment = The path to data files and directories.
+value = /var/lib/radicale/collections
+
+[webmaster_email]
+comment = The webmaster's email address. Used when setting up SSL.
+value = webmaster@example.com