Skip to content

Deploying DAViCal

DAViCal is a robust, open-source CalDAV and CardDAV server that provides centralized calendar and contact management for individuals, teams, and organizations. Built on PostgreSQL and written in PHP, DAViCal implements the CalDAV and CardDAV standards, allowing seamless integration with popular calendar applications like Apple Calendar, Mozilla Thunderbird, Evolution, and mobile devices running iOS and Android.

Unlike proprietary calendar services, DAViCal gives you complete control over your scheduling data while maintaining compatibility with industry standards. The server supports sophisticated features including shared calendars, calendar delegation, free/busy information, recurring events with complex rules, timezone handling, and granular access controls. DAViCal’s adherence to open standards means you can use virtually any CalDAV/CardDAV-compatible client without vendor lock-in.

Why Deploy DAViCal on Klutch.sh?

Klutch.sh provides an excellent platform for hosting DAViCal with several key advantages:

  • Simple Docker Deployment: Deploy your Dockerfile and Klutch.sh automatically handles containerization and scaling
  • HTTP/HTTPS Support: Automatic SSL certificates ensure secure calendar and contact synchronization
  • Persistent Storage: Attach volumes for PostgreSQL data, ensuring your calendars and contacts are never lost
  • Resource Efficiency: DAViCal’s lightweight design means cost-effective hosting for teams of any size
  • Easy Scaling: Adjust resources as your organization grows without complex migration procedures
  • Zero Configuration Networking: Automatic HTTPS endpoints eliminate manual certificate management
  • Database Integration: Run PostgreSQL alongside DAViCal with persistent storage

Prerequisites

Before deploying DAViCal, ensure you have:

  • A Klutch.sh account (sign up at klutch.sh)
  • Git installed locally
  • Basic understanding of CalDAV and CardDAV protocols
  • Familiarity with PostgreSQL database basics
  • A calendar client (Apple Calendar, Thunderbird, or mobile device) for testing
  • Domain access for custom calendar URLs (optional but recommended)

Understanding DAViCal’s Architecture

DAViCal employs a straightforward yet powerful architecture:

Core Components

PHP Application Layer: DAViCal is built with PHP and implements the CalDAV (RFC 4791) and CardDAV (RFC 6352) protocols. The application handles HTTP requests from calendar clients, processes iCalendar data, and manages authentication and authorization.

PostgreSQL Database: All calendar events, contact entries, user accounts, and access permissions are stored in PostgreSQL. The database schema is optimized for efficient querying of calendar data, including complex recurring event calculations.

Apache/Nginx Web Server: A web server handles HTTP requests and passes them to PHP-FPM for processing. The server also manages SSL/TLS encryption for secure data transmission.

AWL Library: DAViCal depends on the Andrew’s Web Libraries (AWL), a collection of PHP utility classes that provide database abstraction, session management, and other foundational features.

Data Model

Principals: Represent users and resources (like conference rooms). Each principal has a unique identifier and associated calendars/address books.

Collections: Container objects that hold calendar events (calendar collections) or contacts (address book collections). Each user typically has multiple collections for different purposes (work calendar, personal calendar, etc.).

Calendar Objects: Individual calendar events stored in iCalendar format. Support for:

  • VEVENT (events and meetings)
  • VTODO (tasks)
  • VJOURNAL (journal entries)
  • VFREEBUSY (free/busy information)
  • VTIMEZONE (timezone data)

Access Control: DAViCal implements WebDAV ACL (RFC 3744) for fine-grained permissions, allowing shared calendars with different privilege levels (read-only, read-write, free-busy only).

Protocol Flow

  1. Calendar client connects via HTTPS to DAViCal server
  2. Client authenticates using Basic Auth or Digest Auth
  3. Client performs PROPFIND to discover available calendars
  4. Client synchronizes using GET (fetch events) and PUT (create/modify events)
  5. Server validates iCalendar data and stores in PostgreSQL
  6. For recurring events, server expands instances based on recurrence rules
  7. Free/busy queries aggregate data across calendars respecting permissions

Calendar Synchronization

DAViCal supports multiple synchronization methods:

  • CalDAV Sync: Efficient incremental sync using ctag and etag headers
  • CardDAV Sync: Contact synchronization with similar efficiency mechanisms
  • WebDAV Extensions: REPORT methods for complex calendar queries
  • Timezone Distribution: Automatic timezone data distribution to clients

Installation and Setup

Step 1: Create the Dockerfile

Create a Dockerfile in your project root:

FROM debian:bullseye-slim
# Install dependencies
RUN apt-get update && apt-get install -y \
apache2 \
libapache2-mod-php \
php \
php-pgsql \
php-xml \
php-curl \
php-yaml \
postgresql-client \
git \
curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Enable Apache modules
RUN a2enmod rewrite headers ssl php7.4
# Set working directory
WORKDIR /usr/share/davical
# Install AWL (Andrew's Web Libraries)
RUN git clone --depth 1 https://gitlab.com/davical-project/awl.git /usr/share/awl
# Install DAViCal
RUN git clone --depth 1 https://gitlab.com/davical-project/davical.git /usr/share/davical
# Create necessary directories
RUN mkdir -p /var/log/davical \
&& chown -R www-data:www-data /var/log/davical
# Copy configuration files
COPY apache-davical.conf /etc/apache2/sites-available/davical.conf
COPY config.php /etc/davical/config.php
# Enable DAViCal site
RUN a2dissite 000-default.conf \
&& a2ensite davical.conf
# Create database initialization script
COPY init-db.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/init-db.sh
# Expose HTTP and HTTPS ports
EXPOSE 80 443
# Create entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD curl -f http://localhost/ || exit 1
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

Step 2: Create Apache Configuration

Create apache-davical.conf:

<VirtualHost *:80>
ServerName davical.example.com
ServerAdmin admin@example.com
DocumentRoot /usr/share/davical/htdocs
DirectoryIndex index.php index.html
<Directory /usr/share/davical/htdocs>
AllowOverride None
Require all granted
# CalDAV/CardDAV specific settings
RewriteEngine On
RewriteBase /
# Redirect caldav.php to the root
RewriteRule ^\.well-known/caldav /caldav.php [R=301,L]
RewriteRule ^\.well-known/carddav /caldav.php [R=301,L]
</Directory>
# PHP settings
php_value include_path /usr/share/awl/inc
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value error_reporting "E_ALL & ~E_NOTICE"
php_value default_charset "utf-8"
# Logging
ErrorLog /var/log/davical/error.log
CustomLog /var/log/davical/access.log combined
LogLevel warn
# Security headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
</VirtualHost>

Step 3: Create DAViCal Configuration

Create config.php:

<?php
/**
* DAViCal Configuration File
*/
// Database connection
$c->pg_connect[] = 'host=postgres port=5432 dbname=davical user=davical_app password=davical_password';
// System settings
$c->system_name = "DAViCal CalDAV Server";
$c->admin_email = "admin@example.com";
$c->enable_row_counting = true;
// Default locale and timezone
$c->default_locale = "en_US";
$c->default_timezone = "UTC";
// Authentication
$c->authenticate_hook = [
'call' => 'LDAP_check',
'config' => [
'host' => 'localhost', // Change for LDAP integration
'port' => 389,
'bindDN' => '',
'passwdAttr' => 'userPassword',
'baseDN' => 'ou=users,dc=example,dc=com',
'filter' => '(uid=%s)',
]
];
// Allow password changes
$c->allow_password_change = true;
// Restrict setup to specific IP (remove in production)
// $c->restrict_setup_to_admin_ip = ['127.0.0.1'];
// Session configuration
$c->session_timeout = 1800; // 30 minutes
// Calendar display options
$c->week_start_day = 1; // 0 = Sunday, 1 = Monday
// Enable CalDAV Scheduling
$c->enable_scheduling = true;
// Free/busy permissions
$c->allow_anonymous_freebusy = false;
// Logging
$c->dbg = [
'ALL' => 0,
'request' => 0,
'query' => 0,
];
// Feature flags
$c->enable_auto_schedule = true;
$c->readonly_webdav_collections = false;
// External URL (update with your domain)
$c->external_url = "https://example-app.klutch.sh";
// Timezone server
$c->timezone_server = "https://www.tzurl.org/zoneinfo-outlook/";
?>

Step 4: Create Database Initialization Script

Create init-db.sh:

#!/bin/bash
set -e
# Wait for PostgreSQL to be ready
until PGPASSWORD=$POSTGRES_PASSWORD psql -h "$POSTGRES_HOST" -U "$POSTGRES_USER" -d postgres -c '\q' 2>/dev/null; do
echo "Waiting for PostgreSQL to be ready..."
sleep 2
done
echo "PostgreSQL is ready!"
# Check if database exists
if ! PGPASSWORD=$POSTGRES_PASSWORD psql -h "$POSTGRES_HOST" -U "$POSTGRES_USER" -d postgres -lqt | cut -d \| -f 1 | grep -qw davical; then
echo "Creating DAViCal database..."
# Create database
PGPASSWORD=$POSTGRES_PASSWORD psql -h "$POSTGRES_HOST" -U "$POSTGRES_USER" -d postgres <<EOF
CREATE DATABASE davical;
CREATE USER davical_app WITH PASSWORD 'davical_password';
CREATE USER davical_dba WITH PASSWORD 'davical_dba_password';
GRANT ALL PRIVILEGES ON DATABASE davical TO davical_dba;
EOF
# Initialize database schema
echo "Initializing database schema..."
PGPASSWORD=davical_dba_password psql -h "$POSTGRES_HOST" -U davical_dba -d davical -f /usr/share/davical/dba/davical.sql
PGPASSWORD=davical_dba_password psql -h "$POSTGRES_HOST" -U davical_dba -d davical -f /usr/share/awl/dba/awl-tables.sql
# Grant permissions
PGPASSWORD=davical_dba_password psql -h "$POSTGRES_HOST" -U davical_dba -d davical <<EOF
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO davical_app;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO davical_app;
EOF
# Create default admin user
PGPASSWORD=davical_password psql -h "$POSTGRES_HOST" -U davical_app -d davical <<EOF
INSERT INTO usr (username, password, fullname, email, active, admin)
VALUES ('admin', '**admin', 'Administrator', 'admin@example.com', true, true);
EOF
echo "Database initialization complete!"
else
echo "Database already exists, skipping initialization."
fi

Step 5: Create Docker Entrypoint

Create docker-entrypoint.sh:

#!/bin/bash
set -e
# Set default environment variables
export POSTGRES_HOST=${POSTGRES_HOST:-postgres}
export POSTGRES_USER=${POSTGRES_USER:-postgres}
export POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
# Initialize database if needed
/usr/local/bin/init-db.sh
# Start Apache in foreground
echo "Starting Apache web server..."
exec apache2-foreground

Step 6: Create Docker Compose for Local Development

Create docker-compose.yml for local testing:

version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
restart: unless-stopped
davical:
build: .
ports:
- "8080:80"
environment:
POSTGRES_HOST: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
depends_on:
- postgres
volumes:
- davical-logs:/var/log/davical
restart: unless-stopped
volumes:
postgres-data:
davical-logs:

Step 7: Initialize Git Repository

Terminal window
git init
git add Dockerfile apache-davical.conf config.php init-db.sh docker-entrypoint.sh docker-compose.yml
git commit -m "Initial DAViCal deployment configuration"

Step 8: Test Locally

Before deploying to Klutch.sh, test your configuration locally:

Terminal window
# Build and start the containers
docker-compose up -d
# Check logs
docker-compose logs -f davical
# Access DAViCal at http://localhost:8080
# Login with username: admin, password: admin

Deploying to Klutch.sh

Step 1: Deploy PostgreSQL Database

First, deploy a PostgreSQL instance for DAViCal:

  1. Navigate to klutch.sh/app
  2. Click "New Project"
  3. Select PostgreSQL from database templates or use a custom Dockerfile
  4. Configure PostgreSQL with: - Database name: `davical` - Username: `postgres` - Password: Choose a secure password
  5. Select **TCP** as traffic type
  6. Set internal port to **5432**
  7. Add persistent storage with mount path: `/var/lib/postgresql/data` and size: `10GB`
  8. Note the connection details (hostname will be like `postgres-app.klutch.sh:8000`)

Step 2: Push DAViCal Repository to GitHub

Create a new repository on GitHub and push your code:

Terminal window
git remote add origin https://github.com/yourusername/davical-klutch.git
git branch -M master
git push -u origin master

Step 3: Connect Repository to Klutch.sh

  1. Navigate to klutch.sh/app
  2. Click "New Project" and select "Import from GitHub"
  3. Authorize Klutch.sh to access your GitHub repositories
  4. Select your DAViCal repository from the list
  5. Klutch.sh will automatically detect the Dockerfile in your repository

Step 4: Configure Traffic Settings

  1. In the project settings, select **HTTP** as the traffic type
  2. Set the internal port to **80**
  3. Klutch.sh will automatically provision an HTTPS endpoint for your application

Step 5: Add Environment Variables

Configure the following environment variables in your Klutch.sh dashboard:

  • POSTGRES_HOST: Your PostgreSQL hostname (e.g., postgres-app.klutch.sh)
  • POSTGRES_USER: postgres
  • POSTGRES_PASSWORD: Your secure database password
  • POSTGRES_PORT: 8000 (external TCP port from Klutch.sh)

Step 6: Add Persistent Storage

While DAViCal stores most data in PostgreSQL, you may want persistent logs:

  1. In your project settings, navigate to the "Storage" section
  2. Add a volume with mount path: `/var/log/davical` and size: `5GB`

Step 7: Deploy

  1. Review your configuration settings
  2. Click "Deploy" to start the deployment process
  3. Klutch.sh will build your Docker image and deploy the container
  4. Monitor the build logs for any errors
  5. Once deployed, your DAViCal server will be available at `your-app.klutch.sh`

Getting Started with DAViCal

Initial Setup

After your first deployment, complete the initial setup:

  1. Access DAViCal Admin: Navigate to https://your-app.klutch.sh/

  2. First Login: Use the default admin credentials:

    • Username: admin
    • Password: admin
  3. Change Admin Password: Immediately change the default password:

    • Click on “Admin” in the top menu
    • Select “Users”
    • Click on “admin” user
    • Enter a new secure password
    • Click “Update”

Creating Users

Create user accounts for your team:

  1. Log in as admin
  2. Navigate to Admin → Users
  3. Click "Create User"
  4. Fill in user details: - Username: `john` - Full Name: `John Doe` - Email: `john@example.com` - Password: Set a secure password - Active: Check the box
  5. Click "Create" to save the user

Setting Up Your First Calendar

Each user automatically gets a default calendar, but you can create additional calendars:

  1. Log in as the user
  2. Navigate to the calendar view
  3. Click "Create New Collection"
  4. Configure calendar properties: - Name: `Work Calendar` - Display Name: `Work` - Description: `Work-related events` - Type: Calendar
  5. Set permissions if you want to share with others
  6. Click "Create"

Connecting Calendar Clients

Configure your calendar client to connect to DAViCal:

Apple Calendar (macOS/iOS)

  1. Open Calendar app (Settings on iOS)
  2. Go to Preferences → Accounts (or Add Account on iOS)
  3. Select “Add CalDAV Account”
  4. Enter account details:
    • Account Type: Advanced
    • Username: john
    • Password: Your password
    • Server Address: your-app.klutch.sh
    • Server Path: /caldav.php/john/
    • Port: 443
    • Use SSL: Yes

Mozilla Thunderbird with Lightning

  1. Open Thunderbird
  2. Right-click on the calendar list → New Calendar
  3. Select “On the Network”
  4. Choose “CalDAV”
  5. Enter location: https://your-app.klutch.sh/caldav.php/john/calendar/
  6. When prompted, enter username and password
  7. Set calendar name and color

Android (via DAVx⁵)

  1. Install DAVx⁵ from F-Droid or Play Store
  2. Open DAVx⁵ and add account
  3. Select “Login with URL and username”
  4. Enter:
    • Base URL: https://your-app.klutch.sh/caldav.php/john/
    • Username: john
    • Password: Your password
  5. DAVx⁵ will discover calendars and contacts
  6. Select which collections to sync
  7. Calendars appear in your device’s calendar app

Evolution (Linux)

  1. Open Evolution
  2. Go to File → New → Calendar
  3. Select “CalDAV”
  4. Enter:
    • URL: https://your-app.klutch.sh/caldav.php/john/calendar/
    • User: john
    • Password: Your password
  5. Click “Apply”

Connecting Contact Clients (CardDAV)

For contact synchronization:

Apple Contacts (macOS/iOS)

  1. System Preferences → Internet Accounts (Settings on iOS)
  2. Add CardDAV Account
  3. Enter:
    • Server: your-app.klutch.sh
    • Username: john
    • Password: Your password

Thunderbird with CardBook

  1. Install CardBook add-on
  2. Right-click in CardBook → New Address Book
  3. Select “Remote” → “CardDAV”
  4. Enter: https://your-app.klutch.sh/caldav.php/john/addresses/

Creating and Managing Events

Once connected, you can create events directly in your calendar client:

  1. Create Event: Click a date/time in your calendar app
  2. Set Details:
    • Title: “Team Meeting”
    • Location: “Conference Room A”
    • Date/Time: Select date and time
    • Attendees: Add email addresses
  3. Save: Event syncs to DAViCal server
  4. Edit: Changes sync bidirectionally

Sharing Calendars

Share calendars with team members:

  1. Log into DAViCal web interface as calendar owner
  2. Navigate to Admin → Calendar Collections
  3. Select the calendar to share
  4. Click "Privileges" tab
  5. Add principal (user or group)
  6. Set permissions: - **Read**: View events - **Read Free/Busy**: See only free/busy status - **Write**: Create and edit events - **All**: Full control
  7. Click "Grant" to save

Shared calendar URL format:

https://your-app.klutch.sh/caldav.php/john/work-calendar/

Free/Busy Information

Query free/busy information for scheduling:

  1. From Web Interface: Navigate to “Free/Busy Lookup”
  2. Enter email or username of person to check
  3. Select date range
  4. View availability displayed graphically

Calendar clients typically handle this automatically when inviting attendees.

Production Best Practices

Security Hardening

  1. Use Strong Passwords: Enforce password policies for all users

  2. Update Default Credentials: Change all default passwords immediately after deployment

  3. Enable HTTPS Only: Ensure all connections use SSL/TLS:

Update apache-davical.conf:

<VirtualHost *:80>
ServerName your-app.klutch.sh
Redirect permanent / https://your-app.klutch.sh/
</VirtualHost>
<VirtualHost *:443>
ServerName your-app.klutch.sh
SSLEngine on
# Klutch.sh handles SSL certificates automatically
# ... rest of configuration
</VirtualHost>
  1. Restrict Admin Access: Limit admin interface to specific IPs if possible:
// In config.php
$c->restrict_setup_to_admin_ip = ['your.admin.ip.address'];
  1. Database Security: Use strong PostgreSQL passwords and restrict connections:
-- In PostgreSQL
ALTER USER davical_app WITH PASSWORD 'very-strong-random-password';
ALTER USER davical_dba WITH PASSWORD 'another-strong-password';
  1. Security Headers: Enhance security headers in Apache:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"

Database Optimization

  1. PostgreSQL Configuration: Optimize PostgreSQL settings for your workload

Create postgresql.conf customizations:

# Memory settings
shared_buffers = 256MB
effective_cache_size = 1GB
work_mem = 16MB
maintenance_work_mem = 128MB
# Connection settings
max_connections = 100
# Write-ahead log
wal_buffers = 16MB
checkpoint_completion_target = 0.9
# Query tuning
random_page_cost = 1.1
effective_io_concurrency = 200
  1. Regular Maintenance: Schedule database maintenance:
pg-maintenance.sh
#!/bin/bash
PGPASSWORD=$POSTGRES_PASSWORD psql -h $POSTGRES_HOST -U $POSTGRES_USER -d davical <<EOF
VACUUM ANALYZE;
REINDEX DATABASE davical;
EOF

Run weekly via cron or scheduled task.

  1. Connection Pooling: For high-traffic deployments, use PgBouncer for connection pooling

Backup Strategy

Implement comprehensive backups:

  1. Database Backups: Regular PostgreSQL backups:
backup-davical.sh
#!/bin/bash
BACKUP_DIR="/backups/davical_$(date +%Y%m%d_%H%M%S)"
mkdir -p $BACKUP_DIR
# Backup database
PGPASSWORD=$POSTGRES_PASSWORD pg_dump -h $POSTGRES_HOST -U $POSTGRES_USER \
-d davical -F c -f $BACKUP_DIR/davical.dump
# Backup configuration
tar -czf $BACKUP_DIR/config.tar.gz /etc/davical/config.php
# Keep only last 30 days
find /backups -name "davical_*.dump" -mtime +30 -delete
find /backups -name "config.tar.gz" -mtime +30 -delete
  1. Automated Backups: Schedule daily backups:
Terminal window
# Add to crontab
0 2 * * * /usr/local/bin/backup-davical.sh
  1. Test Restores: Regularly test backup restoration:
Terminal window
# Restore from backup
PGPASSWORD=$POSTGRES_PASSWORD pg_restore -h $POSTGRES_HOST -U $POSTGRES_USER \
-d davical -c /backups/davical_20231201_020000/davical.dump

Performance Optimization

  1. Apache Tuning: Optimize Apache for CalDAV workload:
# In apache2.conf or mods-enabled/mpm_prefork.conf
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 150
MaxConnectionsPerChild 0
</IfModule>
# Enable compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json
</IfModule>
  1. PHP Optimization: Tune PHP settings:
; In php.ini or .user.ini
memory_limit = 256M
max_execution_time = 60
upload_max_filesize = 10M
post_max_size = 10M
; OPcache settings
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
  1. Database Indexes: Ensure proper indexes exist:
-- Check and create indexes
CREATE INDEX IF NOT EXISTS caldav_data_user_no ON caldav_data(user_no);
CREATE INDEX IF NOT EXISTS caldav_data_dav_name ON caldav_data(dav_name);
CREATE INDEX IF NOT EXISTS caldav_data_caldav_type ON caldav_data(caldav_type);
  1. Caching: Enable opcache and query result caching

Monitoring and Logging

  1. Apache Logs: Monitor access and error logs:
Terminal window
# View real-time logs
tail -f /var/log/davical/access.log
tail -f /var/log/davical/error.log
# Analyze common errors
grep -i "error" /var/log/davical/error.log | tail -20
  1. Database Monitoring: Track database performance:
-- View active connections
SELECT count(*) FROM pg_stat_activity WHERE datname = 'davical';
-- View slow queries
SELECT pid, now() - pg_stat_activity.query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active' AND now() - pg_stat_activity.query_start > interval '5 seconds';
-- View table sizes
SELECT relname, pg_size_pretty(pg_total_relation_size(relid))
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
  1. Health Check Endpoint: Monitor DAViCal availability:
Terminal window
# Simple health check
curl -f https://your-app.klutch.sh/ || echo "DAViCal is down!"
# CalDAV-specific check
curl -X PROPFIND -u john:password https://your-app.klutch.sh/caldav.php/john/ \
-H "Depth: 0" -H "Content-Type: application/xml"
  1. Log Rotation: Implement log rotation to manage disk space:
/etc/logrotate.d/davical
/var/log/davical/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
systemctl reload apache2 > /dev/null 2>&1 || true
endscript
}

Resource Allocation

Recommended resources based on usage:

Small Team (5-20 users):

  • Memory: 1GB - 2GB
  • CPU: 1 vCPU
  • Storage: 10GB
  • Database: 5GB

Medium Organization (20-100 users):

  • Memory: 2GB - 4GB
  • CPU: 2 vCPU
  • Storage: 20GB
  • Database: 10GB

Large Organization (100+ users):

  • Memory: 4GB - 8GB
  • CPU: 4 vCPU
  • Storage: 50GB
  • Database: 20GB+

Troubleshooting

Cannot Connect from Calendar Client

Symptoms: Calendar client shows “Unable to verify account” or connection timeout

Solutions:

  1. Verify URL Format: Ensure correct CalDAV URL:

    https://your-app.klutch.sh/caldav.php/username/
  2. Check Credentials: Verify username and password are correct

  3. Test with curl:

    Terminal window
    curl -u john:password -X PROPFIND https://your-app.klutch.sh/caldav.php/john/ \
    -H "Depth: 1" -H "Content-Type: application/xml"
  4. Check Apache Logs:

    Terminal window
    tail -f /var/log/davical/error.log
  5. Verify SSL Certificate: Ensure HTTPS is working properly:

    Terminal window
    curl -v https://your-app.klutch.sh/

Authentication Failures

Symptoms: “401 Unauthorized” errors when connecting

Solutions:

  1. Verify User Exists: Check database:

    SELECT username, active FROM usr WHERE username = 'john';
  2. Reset Password: Update password in database:

    UPDATE usr SET password = '**newpassword' WHERE username = 'john';

    Note: DAViCal uses ** prefix for plaintext passwords that will be hashed

  3. Check LDAP Configuration (if using LDAP):

    Terminal window
    # Test LDAP bind
    ldapsearch -x -H ldap://ldap.example.com -D "uid=john,ou=users,dc=example,dc=com" -W
  4. Enable Debug Logging: In config.php:

    $c->dbg = [
    'ALL' => 1,
    'request' => 1,
    'query' => 1,
    ];

Events Not Syncing

Symptoms: Changes made in one client don’t appear in another

Solutions:

  1. Check Sync Interval: Verify client sync settings (should be automatic or every 5-15 minutes)

  2. Force Refresh: In calendar client, right-click calendar and select “Refresh”

  3. Verify Collection Path: Ensure all clients use the same calendar URL

  4. Check Database Updates:

    SELECT caldav_data, modified FROM caldav_data
    WHERE user_no = (SELECT user_no FROM usr WHERE username = 'john')
    ORDER BY modified DESC LIMIT 10;
  5. Clear Client Cache: Remove and re-add calendar in client

Database Connection Errors

Symptoms: “Failed to connect to database” or PostgreSQL connection errors

Solutions:

  1. Verify PostgreSQL is Running:

    Terminal window
    pg_isready -h $POSTGRES_HOST -p 8000
  2. Check Connection String: Verify in config.php:

    $c->pg_connect[] = 'host=postgres-app.klutch.sh port=8000 dbname=davical user=davical_app password=your_password';
  3. Test Direct Connection:

    Terminal window
    PGPASSWORD=your_password psql -h postgres-app.klutch.sh -p 8000 -U davical_app -d davical -c "SELECT 1;"
  4. Check PostgreSQL Logs: Review database logs for connection issues

  5. Verify Network Connectivity: Ensure DAViCal container can reach PostgreSQL:

    Terminal window
    telnet postgres-app.klutch.sh 8000

Recurring Events Not Displaying

Symptoms: Recurring events show only first instance or don’t appear

Solutions:

  1. Check RRULE Format: Verify iCalendar RRULE is valid:

    SELECT caldav_data FROM caldav_data WHERE caldav_data LIKE '%RRULE%' LIMIT 1;
  2. Update Timezone Data: Ensure timezone definitions are current:

    Terminal window
    # Update timezone data in DAViCal
    wget https://www.tzurl.org/zoneinfo-outlook/timezone-defs.ics
    # Import to DAViCal
  3. Client Compatibility: Some clients have issues with complex RRULEs; test with multiple clients

  4. Check Date Range: Ensure client is querying appropriate date range for recurrence

Performance Issues

Symptoms: Slow response times, calendar takes long to load

Solutions:

  1. Check Database Performance:

    -- View slow queries
    SELECT query, mean_exec_time, calls
    FROM pg_stat_statements
    ORDER BY mean_exec_time DESC
    LIMIT 10;
  2. Optimize Large Calendars: Archive old events:

    -- Delete events older than 2 years
    DELETE FROM caldav_data
    WHERE caldav_type = 'VEVENT'
    AND modified < NOW() - INTERVAL '2 years';
  3. Reduce Client Sync Range: Configure clients to sync only last 6-12 months

  4. Check Resource Usage:

    Terminal window
    # Monitor CPU and memory
    top
    # Check disk I/O
    iostat -x 1
  5. Scale Resources: Increase CPU/memory allocation in Klutch.sh dashboard

Advanced Configuration

LDAP Authentication Integration

Integrate with existing LDAP directory:

  1. Update Configuration in config.php:
$c->authenticate_hook = [
'call' => 'LDAP_check',
'config' => [
'host' => 'ldap.example.com',
'port' => 389,
'bindDN' => 'cn=davical,ou=services,dc=example,dc=com',
'bindpw' => 'service-account-password',
'baseDN' => 'ou=users,dc=example,dc=com',
'filter' => '(uid=%s)',
'mapping' => [
'username' => 'uid',
'fullname' => 'cn',
'email' => 'mail',
],
'group_base_dn' => 'ou=groups,dc=example,dc=com',
'group_filter' => '(memberUid=%s)',
]
];
// Enable LDAP group-based access
$c->authorize_hook = ['call' => 'LDAP_group_check'];
  1. Install PHP LDAP Extension:
# Add to Dockerfile
RUN apt-get install -y php-ldap
  1. Test LDAP Integration:
Terminal window
ldapsearch -x -H ldap://ldap.example.com -D "cn=davical,ou=services,dc=example,dc=com" \
-W -b "ou=users,dc=example,dc=com" "(uid=john)"

Custom Domain and Branding

  1. Custom Domain: Configure your domain in Klutch.sh dashboard

  2. Update Configuration:

// In config.php
$c->external_url = "https://calendar.yourdomain.com";
$c->system_name = "Your Company Calendar";
$c->admin_email = "calendar-admin@yourdomain.com";
  1. Custom Logo: Replace default logo:
Terminal window
# Copy your logo
cp your-logo.png /usr/share/davical/htdocs/images/logo.png
  1. Custom CSS: Create custom stylesheet:
custom.css
body {
font-family: 'Your-Font', sans-serif;
}
.header {
background-color: #your-brand-color;
}

Add to Apache config:

Alias /custom.css /usr/share/davical/htdocs/custom.css

Calendar Delegation

Allow users to delegate calendar access:

  1. Enable in Configuration:
$c->enable_delegation = true;
  1. Set Delegates via Web Interface:

    • User logs in
    • Navigates to “Relationships”
    • Adds delegate with permissions
  2. Calendar URL for Delegates:

    https://your-app.klutch.sh/caldav.php/john/?delegate=secretary

Resource Booking (Conference Rooms)

Set up bookable resources:

  1. Create Resource Account:
INSERT INTO usr (username, password, fullname, email, active)
VALUES ('conf-room-a', '**password', 'Conference Room A', 'rooms@example.com', true);
-- Mark as resource
INSERT INTO resource (user_no, resource_type)
VALUES ((SELECT user_no FROM usr WHERE username = 'conf-room-a'), 'room');
  1. Configure Resource Properties:
UPDATE usr SET schedule_transp = 'opaque' WHERE username = 'conf-room-a';
  1. Set Booking Permissions: Allow users to schedule resources

  2. Access Resource Calendar: https://your-app.klutch.sh/caldav.php/conf-room-a/

Email Notifications

Enable email notifications for calendar invitations:

  1. Configure SMTP in config.php:
$c->email_from = 'calendar@example.com';
$c->smtp_host = 'smtp.example.com';
$c->smtp_port = 587;
$c->smtp_username = 'calendar@example.com';
$c->smtp_password = 'smtp-password';
$c->smtp_security = 'tls';
  1. Enable Scheduling:
$c->enable_scheduling = true;
$c->scheduling_notify = true;
  1. Test Email:
Terminal window
# Send test email via PHP
php -r "mail('test@example.com', 'Test', 'Test message');"

Timezone Management

Maintain accurate timezone data:

  1. Update Timezone Database:
update-timezones.sh
#!/bin/bash
cd /tmp
wget https://www.tzurl.org/zoneinfo-outlook/aliases.txt
wget https://www.tzurl.org/zoneinfo-outlook/timezone-defs.ics
# Import to DAViCal
PGPASSWORD=$POSTGRES_PASSWORD psql -h $POSTGRES_HOST -U davical_app -d davical <<EOF
-- SQL to import timezone data
EOF
  1. Configure Timezone Server:
$c->timezone_server = "https://www.tzurl.org/zoneinfo-outlook/";

API Access and Automation

Automate DAViCal operations:

  1. Create Events via curl:
create-event.sh
#!/bin/bash
USERNAME="john"
PASSWORD="password"
CALENDAR_URL="https://your-app.klutch.sh/caldav.php/john/calendar/"
# iCalendar event data
EVENT_DATA='BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp//CalDAV Client//EN
BEGIN:VEVENT
UID:20231201T120000-john@example.com
DTSTAMP:20231201T120000Z
DTSTART:20231215T140000Z
DTEND:20231215T150000Z
SUMMARY:Team Meeting
DESCRIPTION:Weekly team sync
LOCATION:Conference Room
END:VEVENT
END:VCALENDAR'
# Create event
curl -X PUT -u "$USERNAME:$PASSWORD" \
-H "Content-Type: text/calendar" \
--data "$EVENT_DATA" \
"$CALENDAR_URL/20231201T120000-john@example.com.ics"
  1. Query Events:
Terminal window
# Fetch all events
curl -X REPORT -u john:password \
-H "Depth: 1" \
-H "Content-Type: application/xml" \
--data '<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop xmlns:D="DAV:">
<D:getetag/>
<C:calendar-data/>
</D:prop>
<C:filter>
<C:comp-filter name="VCALENDAR">
<C:comp-filter name="VEVENT"/>
</C:comp-filter>
</C:filter>
</C:calendar-query>' \
https://your-app.klutch.sh/caldav.php/john/calendar/
  1. Python Script Example:
#!/usr/bin/env python3
import caldav
from datetime import datetime, timedelta
# Connect to DAViCal
client = caldav.DAVClient(
url="https://your-app.klutch.sh/caldav.php/john/",
username="john",
password="password"
)
principal = client.principal()
calendars = principal.calendars()
# Create event
cal = calendars[0]
event = cal.save_event(
dtstart=datetime.now() + timedelta(days=1),
dtend=datetime.now() + timedelta(days=1, hours=1),
summary="Automated Meeting",
description="Created by script"
)
print(f"Event created: {event}")
# Query events
events = cal.date_search(
start=datetime.now(),
end=datetime.now() + timedelta(days=30)
)
for event in events:
print(f"Event: {event.instance.vevent.summary.value}")

Multi-Tenancy Setup

Support multiple organizations:

  1. Create Separate Databases: One database per tenant

  2. Use Subdirectories: Different paths for each tenant:

    • https://your-app.klutch.sh/tenant1/
    • https://your-app.klutch.sh/tenant2/
  3. Apache Virtual Hosts: Configure multiple virtual hosts

  4. Separate Config Files: Each tenant has own config.php

Additional Resources

Conclusion

DAViCal provides a robust, standards-compliant solution for centralized calendar and contact management. By deploying on Klutch.sh, you gain the benefits of automatic SSL, persistent storage, and simple Docker-based deployment while maintaining complete control over your scheduling data.

The server’s adherence to CalDAV and CardDAV standards ensures compatibility with virtually every calendar client—from Apple devices to Android, from Thunderbird to Evolution. Features like shared calendars, calendar delegation, resource booking, and free/busy lookups make DAViCal suitable for teams of any size, from small businesses to large enterprises.

Whether you’re replacing a proprietary calendar service, building a new collaboration platform, or providing calendar services for your organization, DAViCal on Klutch.sh offers the reliability, security, and flexibility you need. Start with the basic configuration outlined in this guide, then expand functionality through LDAP integration, custom branding, and advanced scheduling features.

Your calendar data remains under your control, synchronization is efficient and reliable, and the open-source nature of DAViCal means you can audit, customize, and extend the platform to meet your exact requirements. Deploy DAViCal today and take control of your organization’s scheduling infrastructure.