This time we will install and configure a calendar hosting server, specifically Baikal
.
First, we will create a clean jail
where we will install the basic configuration and tools I usually set up, and then proceed with PHP
, Nginx
, and MySQL
.
The tutorial consists of the following steps:
- Jail and installation of basic tools.
- Load balancer configuration.
- PHP-FPM installation.
- Nginx installation.
- MySQL installation.
- Baikal installation.
- PC client.
- Android client.
- Integration with AwesomeWM.
- Troubleshooting.
Jail and installation of basic tools:
We create the jail
and assign an IP
:
bastille create -T TimeGuard 14.3-RELEASE 192.168.69.18/24 nfe0
We install the basic tools using the template system :
bastille template TimeGuard datadyne.alfaexploit.com/bastille-basicconfiguration
Load balancer configuration:
If you have a load balancer, the best way to encrypt traffic is on the load balancer itself. First, create the DNS
entry for the domain we will use for the calendar server.
In my case:
baikal.alfaexploit.com
My previous certificates were issued via ACME
, so I just need to add one more domain:
/root/.acme.sh/acme.sh --issue --standalone --httpport 88 -d alfaexploit.com -d www.alfaexploit.com -d badguys.alfaexploit.com -d mail.alfaexploit.com -d grafana.alfaexploit.com -d redbulltank.alfaexploit.com -d baikal.alfaexploit.com --renew-hook 'cat /root/.acme.sh/alfaexploit.com_ecc/fullchain.cer /root/.acme.sh/alfaexploit.com_ecc/alfaexploit.com.key > /usr/local/etc/haproxy_certs.pem && haproxy -c -f /usr/local/etc/haproxy.conf && service haproxy restart && cp /root/.acme.sh/alfaexploit.com_ecc/fullchain.cer /root/.acme.sh/alfaexploit.com_ecc/alfaexploit.com.key /root/.acme.sh/alfaexploit.com_ecc/ca.cer /usr/local/www/alfaexploit_certs_mail/ && chmod 644 /usr/local/www/alfaexploit_certs_mail/* && service nginx restart' --force
We check that the certificate has the correct names:
openssl x509 -in /usr/local/etc/haproxy_certs.pem -noout -text | egrep -A1 "Subject:|Subject Alternative Name"
DNS:alfaexploit.com, DNS:badguys.alfaexploit.com, DNS:baikal.alfaexploit.com, DNS:grafana.alfaexploit.com, DNS:mail.alfaexploit.com, DNS:redbulltank.alfaexploit.com, DNS:www.alfaexploit.com
We configure HAProxy
; pay attention to detail, the origin connection information is sent
via ProxyProtocol
(send-proxy-v2):
vi /usr/local/etc/haproxy.conf
backend baikal
server baikal 192.168.69.18:80 check send-proxy-v2
Restart the service:
service haproxy restart
PHP-FPM installation:
Install PHP8.4
and the extensions needed for Baikal
to work properly:
pkg install -y php84 php84-session php84-pdo php84-pdo_mysql php84-filter php84-xml php84-dom php84-xmlreader php84-xmlwriter
Configure the TimeZone
:
cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini
vi /usr/local/etc/php.ini
date.timezone = Europe/Madrid
Enable and start the PHP-FPM
service:
sysrc php_fpm_enable=yes
service php_fpm start
Nginx installation:
Install Nginx
:
pkg install -y nginx
Configure a Vhost
for Baikal
, paying attention to the proxyprotocol
detail (proxy_protocol/set_real_ip_from/real_ip_header):
vi /usr/local/etc/nginx/baikal.conf
server {
listen 80 proxy_protocol;
server_name baikal.alfaexploit.com;
set_real_ip_from 192.168.69.19;
real_ip_header proxy_protocol;
root /usr/local/www/baikal/html;
index index.php;
rewrite ^/.well-known/caldav /dav.php redirect;
rewrite ^/.well-known/carddav /dav.php redirect;
charset utf-8;
location ~ /(\.ht|Core|Specific|config) {
deny all;
return 404;
}
location ~ ^(.+\.php)(.*)$ {
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
Include the configuration from the main file:
vi /usr/local/etc/nginx/nginx.conf
http {
include mime.types;
default_type application/octet-stream;
include baikal.conf;
....
Check that PHP
execution works:
mkdir -p /usr/local/www/baikal/html/
echo "<?php phpinfo(); ?>" > /usr/local/www/baikal/html/info.php
Enable and start the Nginx
service:
sysrc nginx_enable=YES
service nginx restart
Access PHP
, you should see something like this:
https://baikal.alfaexploit.com/info.php
Remove the PHP
test file once verified:
rm /usr/local/www/baikal/html/info.php
Reload the Nginx
configuration:
service nginx reload
MySQL installation:
Install MySQL
:
pkg install -y mysql80-server percona-toolkit
Enable and start the MySQL
service:
sysrc mysql_enable=YES
service mysql-server start
Secure the installation:
mysql_secure_installation
Create a client configuration file to avoid entering the password each time:
vi .my.cnf
[client]
user = root
password = PASSWORD
Restrict file permissions:
chmod 600 .my.cnf
Create the database and user:
mysql
CREATE DATABASE baikal;
CREATE USER baikal@'192.168.69.18' IDENTIFIED WITH mysql_native_password BY 'PASSWORD';
GRANT ALL PRIVILEGES ON baikal.* TO baikal@'192.168.69.18';
FLUSH PRIVILEGES;
exit;
Baikal installation:
Keep in mind that Baikal
is only intended to manage calendars, not to edit events. Therefore, do not expect to create or edit events from the web interface.
Install Baikal
:
cd /usr/local/www/
rm -rf baikal
wget https://github.com/sabre-io/Baikal/releases/download/0.10.1/baikal-0.10.1.zip
unzip baikal-0.10.1.zip
rm baikal-0.10.1.zip
Set permissions for the Specific
and config
directories:
chown -R www:www baikal/Specific
chown -R www:www baikal/config
Access the Baikal
web interface and perform the initial setup:
http://baikal.alfaexploit.com
![]() |
![]() |
![]() |
Log in and create a user:
![]() |
![]() |
![]() |
![]() |
Edit the default calendar:
![]() |
![]() |
![]() |
PC client:
We will use Khal
as the client, which is available for both Debian
and FreeBSD
:
apt install khal vdirsyncer
pkg install -y py311-khal
In my case, I am on Debian
, so configuration may differ slightly on FreeBSD
, especially paths:
mkdir -p ~/.config/vdirsyncer
vi ~/.config/vdirsyncer/config
[general]
status_path = "~/.local/share/vdirsyncer/status/"
[pair baikal]
a = "baikal_local"
b = "baikal_remote"
collections = ["from b"]
conflict_resolution = "b wins"
[storage baikal_local]
type = "filesystem"
path = "~/.local/share/khal/calendars/"
fileext = ".ics"
[storage baikal_remote]
type = "caldav"
url = "https://baikal.alfaexploit.com/dav.php/calendars/kr0m/default/"
username = "kr0m"
password = "PASSWORD"
auth = "digest"
Khal
is just the calendar viewer and editor, while Vdirsyncer
is responsible for keeping them synchronized.
Discover the baikal
server defined in the configuration:
vdirsyncer discover baikal
Discovering collections for pair baikal
baikal_local:
baikal_remote:
- "default" ("Default calendar")
warning: No collection "default" found for storage baikal_local.
Should vdirsyncer attempt to create it? [y/N]: y
Saved for baikal: collections = ["default"]
Synchronize:
vdirsyncer sync
Syncing baikal/default
Configure Khal
:
khal configure
What ordering of year, month, date do you want to use?
[0] year-month-day (today: 2025-08-25)
[1] day/month/year (today: 25/08/2025)
[2] month/day/year (today: 08/25/2025)
[3] Custom
Please choose one of the above options: 1
Date format: %d/%m/%Y (today as an example: 25/08/2025)
What timeformat do you want to use?
[0] 24 hour clock (recommended)
[1] 12 hour clock
Please choose one of the above options [0]: 0
Time format: %H:%M (current time as an example: 11:50)
[0] Create a new calendar on this computer
[1] Use a calendar already on this computer (vdir format)
[2] Sync a calendar from the internet (CalDAV format, requires vdirsyncer)
Please choose one of the above options: 1
The following calendars were found:
Found 1 calendars from vdirsyncer
baikal_local: ~/.local/share/khal/calendars/*
Use these calendars for khal? [Y/n]: Y
Which calendar do you want as a default calendar?
(The default calendar is used when no calendar is specified.)
Configured calendars: default
Please type one of the above options (default) [default]:
Do you want to write the config to ~/.config/khal/config? (Choosing `No` will abort) [Y/n]: Y
Successfully wrote configuration to ~/.config/khal/config
Now we can create events on the calendar:
khal new 25/08/2025 12:00 25/08/2025 13:00 "Coders meeting" ":: Arguee about some code issues"
Check the events:
khal list
Today, 25/08/2025
12:00-13:00 Coders meeting :: Arguee about some code issues
If synchronized, changes will be uploaded to the server:
vdirsyncer sync
Syncing baikal/default
Copying (uploading) item WU9QARLFCN7O0Q9WPC5KE9TIUQTOBE4O0HJ2 to baikal_remote/default
We can also use a console interface that responds to clicks to create events:
ikhal
To create a new event on the selected day, press n
:
![]() |
![]() |
![]() |
From the CLI we can also view events, e.g., the next 30 days:
khal list today 30d
Today, 25/08/2025
12:00-13:00 Coders meeting :: Arguee about some code issues
Friday, 29/08/2025
Test event ⟳ :: Test description
Synchronizing uploads items to the server:
vdirsyncer sync
Syncing baikal/default
Copying (uploading) item Q4MFESW0UBYV89RLTW7E5Y2QJ5ILY02VC1ZB to baikal_remote/default
To avoid manually synchronizing, schedule it with crontab
:
crontab -e
* * * * * vdirsyncer sync
Android client:
As on the PC, on Android we need software to synchronize calendars and another to manage events.
For synchronization, we use DAVx⁵
from
F-Droid
.
Follow the initial setup:
![]() |
![]() |
![]() |
![]() |
![]() |
Add an account:
url: https://baikal.alfaexploit.com/dav.php/calendars/kr0m/default/
username: kr0m
password: PASSWORD
![]() |
![]() |
![]() |
![]() |
![]() |
Change synchronization times and disable the VPN
:
![]() |
![]() |
Keep both the Default calendar
and Default Address Book
enabled:
![]() |
![]() |
For event management, we use Etar
from GooglePlay
, granting permissions:
![]() |
![]() |
![]() |
When creating an event, ensure it is on the correct calendar:
Synchronize on the PC and list events
vdirsyncer sync
Syncing baikal/default
Copying (uploading) item 180d4f84-3e1b-47cf-8428-470448b9fd74 to baikal_local/default
Deleting item f9ae1b68-9517-4e43-b8ea-ed2329a7d400 from baikal_local/default
khal list today 30d
Today, 25/08/2025
Test etar :: Test etar
Wednesday, 27/08/2025
Hhhhh ⏰ :: Hhhhhh
Friday, 29/08/2025
test ⟳ :: AAA
Integration with AwesomeWM:
Simply clone the repository of widgets I have on GitHub
:
cd ~/.config/awesome
git clone https://github.com/ARPABoy/kr0mWidgets.git
And follow the
instructions described on GitHub
.
The final result will look like this:
Troubleshooting:
First, make sure the event is being created on the correct calendar from Etar
.
If you want to reset the configuration on the PC
, run the following commands:
rm -rf ~/.local/share/khal/calendars/*
rm ~/.config/vdirsyncer/config