Radicale
Radicale is a CalDAV/CardDAV server. It can be used to store your contacts and calendars, and i will show you how to install also InfCloud as a WEB GUI to it.
CalDAV is the WebDAV extension to manage Calendars
CardDAV is the WebDAV extension to manage address books and contacts in general.
To add support for both, which will allow your phone to sync contacts and calendars with your home server, i choose to use the great, simple and effective Radicale server.
Please note that Radicale do not provide a user-interface to edit or use calendars or contacts, you need third party apps for that, and the one i am currently using is InfCloud, see installation instructions at the bottom of this page.
To install radicale, of course, you need it's dedicated user, so add user:
useradd -d /data/daemons/radicale -m radicale
Create data folder:
mkdir /data/cardcal chown radicale:radicale /data/cardcal
Radicale uses pip, so as usual enable it on Gentoo for user Radicale by creating the file /data/daemons/radicale/.config/pip/pip.conf with this content:
- pip.conf
[global] break-system-packages = true user = true
Install as user radicale:
su - radicale pip install --upgrade radicale
Create the config file ~/.config/radicale/config:
- config
[server] # Bind all addresses hosts = 127.0.0.1:5232 [auth] type = http_x_remote_user #htpasswd_filename = ~/.config/radicale/users #htpasswd_encryption = md5 [storage] filesystem_folder = /data/cardcal
then start it:
su - radicale
radicale
Reverse Proxy
Radicale can be hosted both on subdomain or subpath, but i choose subdomain to make it easier to also host InfCloud on the same subdomain.
As usual you want it protected by the Reverse Proxy, so create the radicale.conf file:
- radicale.conf
server { server_name radicale.mydomain.com; listen 443 ssl; listen 8443 ssl; http2 on; include "com.mydomain/authelia_location-basic.conf"; access_log /var/log/nginx/radicale.mydomain.com_access_log main; error_log /var/log/nginx/radicale.mydomain.com_error_log info; location / { # The trailing / is important! proxy_pass http://127.0.0.1:5232/; # The / is important! proxy_set_header X-Script-Name /; proxy_set_header X-Remote-User $remote_user; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_pass_header Authorization; include "com.mydomain/authelia_proxy.conf"; include "com.mydomain/authelia_authrequest-basic.conf"; } location /gui/ { # The trailing / is important! proxy_pass http://127.0.0.1:5233/; # The / is important! proxy_set_header X-Script-Name /; proxy_set_header X-Remote-User $remote_user; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_pass_header Authorization; } include com.mydomain/certbot.conf; }
add this config file to NGINX (see The Reverse Proxy concept for more details) and restart nginx.
This set-up links also Radicale to your SSO (see this page, which is a good approach, but please note that this will make access to the radicale web ui (not the WEB GUI InfCloud) a bit more awkward as the Radicale login window cannot be disabled, and the user will need double authentication.
Now go with browser to https://radicale.mydomain.com to finish setup.
URLs summary
The following URL's are exposed between Radicale and InfCloud:
- https://radicale.mydomain.com: server web access, will redirect to “.web” and let you create calendars
- https://radicale.mydomain.com/gui/: InfCloud, client GUI to actually edit and access your calendars and contacts
And, last, when setting up your *DAV clients (like on Android or similar), use https://radicale.mydomain.com as server URL.
Startup
Since i use OpenRC, to start radicale simply create the following script under /etc/init.d:
- /etc/init.d/radicale
#!/sbin/openrc-run # Copyright 1999-2021 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 description="Radicale WebDAV / WebCAL server" pidfile="/run/radicale.pid" command_background=true command="/data/daemons/radicale/.local/bin/radicale" command_args="" command_user="radicale:users" depend() { need net }
Make it executable and add the service to the default runlevel:
chmod +x /etc/init.d/radicale rc-update add radicale default
Sharing calendars
Radicale doesn't directly support sharing calendars (see here) but it's pretty easy to share calendars between users anyway by creating simple symlinks.
So, first of all login on Radicale web GUI with all the users you want to share the calendar with, then create in one of them the actual calendar.
At this point, you only need to link the calendar folder inside all he users:
ln -s /data/cardcal/collection-root/user1/72757af3-557c-e8e4-bdd7-c9bc5689b862 /data/cardcal/collection-root/user2/
and that's it.
WEB GUI for Radicale
InfCloud is a nice javascript WEB GUI for Radicale. It's a bit old-style, but solid and pretty usable. I strongly suggest to go the container way and use this InfCloud docker solution here, which is pretty nice and easy to setup.
First of all, to avoid cross-domain issues (CORS) which are hard to solve due to using SSO on Radicale itself, i will host InfCloud on the same radicale domain, using the /gui/ subpath.
First of all, setup the docker-compose file /data/daemons/radicale/docker-compose.yml:
- docker-compose.yml
version: "3" services: infcloud: image: ckulka/infcloud restart: always depends_on: - php ports: - "5233:80" volumes: - infcloud:/usr/share/nginx/infcloud - ./config.js:/usr/share/nginx/infcloud/config.js:ro php: image: php:7.3-fpm-alpine restart: always volumes: - infcloud:/usr/share/nginx/infcloud:ro volumes: infcloud:
Now, create the /data/daemons/radicale/config.js:
- config.js
var globalNetworkCheckSettings={ href: "https://radicale.mydomain.com/", timeOut: 90000, lockTimeOut: 10000, checkContentType: true, settingsAccount: true, delegation: true, additionalResources: [], hrefLabel: null, forceReadOnly: null, ignoreAlarms: false, backgroundCalendars: [] }; var globalInterfaceLanguage='it_IT'; var globalSearchTransformAlphabet={ '[ÀàÁáÂâÄ䯿ÃãÅåĀā]': 'a', '[ÇçĆćČč]': 'c', '[Ďď]': 'd', '[ÈèÉéÊêËëĒēĖėĘęĚě]': 'e', '[Ğğ]': 'g', '[ÌìÍíÎîİıÏïĪīĮį]': 'i', '[ŁłĹ弾]': 'l', '[ŃńÑñŇň]': 'n', '[ÒòÓóÔôÖöŐőŒœØøÕõŌō]': 'o', '[ŔŕŘř]': 'r', '[ŚśŠšȘșŞşẞß]': 's', '[ŤťȚțŢţ]': 't', '[ÙùÚúÛûÜüŰűŮůŪū]': 'u', '[ÝýŸÿ]': 'y', '[ŹźŻżŽž]': 'z' }; var globalSortAlphabet=' 0123456789'+ 'AÀÁÂÄÆÃÅĀBCÇĆČDĎEÈÉÊËĒĖĘĚFGĞHIÌÍÎİÏĪĮJKLŁĹĽMNŃÑŇOÒÓÔÖŐŒØÕŌ'+ 'PQRŔŘSŚŠȘșŞşẞTŤȚțŢţUÙÚÛÜŰŮŪVWXYÝŸZŹŻŽ'+ 'aàáâäæãåābcçćčdďeèéêëēėęěfgğhiìíîïīįıjklłĺľmnńñňoòóôöőœøõō'+ 'pqrŕřsśšßtťuùúûüűůūvwxyýÿzźżžАБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЮЯ'+ 'Ьабвгґдеєжзиіїйклмнопрстуфхцчшщюяь'; var globalBackgroundSync=true; var globalSyncResourcesInterval=120000; var globalEnableRefresh=false; var globalEnableKbNavigation=true; var globalInterfaceCustomLanguages=[]; var globalResourceAlphabetSorting=true; var globalNewVersionNotifyUsers=[]; var globalDatepickerFirstDayOfWeek=1; var globalHideInfoMessageAfter=1800; var globalEditorFadeAnimation=666; var globalEventStartPastLimit=3; var globalEventStartFutureLimit=3; var globalTodoPastLimit=1; var globalLoadedCalendarCollections=[]; var globalLoadedTodoCollections=[]; var globalActiveCalendarCollections=[]; var globalActiveTodoCollections=[]; var globalActiveView='multiWeek'; var globalOpenFormMode='double'; var globalTodoListFilterSelected=['filterAction', 'filterProgress']; var globalCalendarStartOfBusiness=8; var globalCalendarEndOfBusiness=17; var globalDefaultEventDuration=120; var globalDisplayHiddenEvents=false; var globalTimeZoneSupport=true; var globalTimeZone='Europe/Berlin'; var globalTimeZonesEnabled=[]; var globalRewriteTimezoneComponent=true; var globalRemoveUnknownTimezone=false; var globalShowHiddenAlarms=false; var globalIgnoreCompletedOrCancelledAlarms=true; var globalMozillaSupport=false; var globalWeekendDays=[0, 6]; var globalAppleRemindersMode=true; var globalLoadedAddressbookCollections=[]; var globalActiveAddressbookCollections=[]; var globalCompatibility={anniversaryOutputFormat: ['apple']}; var globalUriHandlerTel='tel:'; var globalUriHandlerEmail='mailto:'; var globalUriHandlerUrl='http://'; var globalUriHandlerProfile={ 'twitter': 'http://twitter.com/%u', 'facebook': 'http://www.facebook.com/%u', 'flickr': 'http://www.flickr.com/photos/%u', 'linkedin': 'http://www.linkedin.com/in/%u', 'myspace': 'http://www.myspace.com/%u', 'sinaweibo': 'http://weibo.com/n/%u' }; var globalDefaultAddressCountry='us'; var globalAddressCountryEquivalence=[ {country: 'de', regex: '^\\W*Deutschland\\W*$'}, {country: 'sk', regex: '^\\W*Slovensko\\W*$'} ]; var globalAddressCountryFavorites=[]; var globalContactStoreFN=['prefix',' last',' middle',' first',' suffix']; var globalGroupContactsByCompanies=false; var globalContactDataMinVisiblePercentage=0.95;
Edit to your likings, more detail can be found here.
Now create the startup config script /etc/conf.d/user-containers.infCloud:
- user-containers.infCloud
USER=radicale DESCRIPTION="WEB GUI for Radicale"
Create the link and start it:
cd /etc/init.d/ ln -s user-containers user-containers.infCloud rc-update add user-containers.infCloud default ./user-containers.infCloud start
Now point your browser to https://radicale.mydomain.com/gui/ to access infCloud.