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
- Calendar client connects via HTTPS to DAViCal server
- Client authenticates using Basic Auth or Digest Auth
- Client performs PROPFIND to discover available calendars
- Client synchronizes using GET (fetch events) and PUT (create/modify events)
- Server validates iCalendar data and stores in PostgreSQL
- For recurring events, server expands instances based on recurrence rules
- 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 dependenciesRUN 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 modulesRUN a2enmod rewrite headers ssl php7.4
# Set working directoryWORKDIR /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 DAViCalRUN git clone --depth 1 https://gitlab.com/davical-project/davical.git /usr/share/davical
# Create necessary directoriesRUN mkdir -p /var/log/davical \ && chown -R www-data:www-data /var/log/davical
# Copy configuration filesCOPY apache-davical.conf /etc/apache2/sites-available/davical.confCOPY config.php /etc/davical/config.php
# Enable DAViCal siteRUN a2dissite 000-default.conf \ && a2ensite davical.conf
# Create database initialization scriptCOPY init-db.sh /usr/local/bin/RUN chmod +x /usr/local/bin/init-db.sh
# Expose HTTP and HTTPS portsEXPOSE 80 443
# Create entrypoint scriptCOPY docker-entrypoint.sh /usr/local/bin/RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Health checkHEALTHCHECK --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/bashset -e
# Wait for PostgreSQL to be readyuntil 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 2done
echo "PostgreSQL is ready!"
# Check if database existsif ! 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 <<EOFCREATE 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 <<EOFGRANT 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 <<EOFINSERT 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."fiStep 5: Create Docker Entrypoint
Create docker-entrypoint.sh:
#!/bin/bashset -e
# Set default environment variablesexport 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 foregroundecho "Starting Apache web server..."exec apache2-foregroundStep 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
git initgit add Dockerfile apache-davical.conf config.php init-db.sh docker-entrypoint.sh docker-compose.ymlgit commit -m "Initial DAViCal deployment configuration"Step 8: Test Locally
Before deploying to Klutch.sh, test your configuration locally:
# Build and start the containersdocker-compose up -d
# Check logsdocker-compose logs -f davical
# Access DAViCal at http://localhost:8080# Login with username: admin, password: adminDeploying to Klutch.sh
Step 1: Deploy PostgreSQL Database
First, deploy a PostgreSQL instance for DAViCal:
- Navigate to klutch.sh/app
- Click "New Project"
- Select PostgreSQL from database templates or use a custom Dockerfile
- Configure PostgreSQL with: - Database name: `davical` - Username: `postgres` - Password: Choose a secure password
- Select **TCP** as traffic type
- Set internal port to **5432**
- Add persistent storage with mount path: `/var/lib/postgresql/data` and size: `10GB`
- 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:
git remote add origin https://github.com/yourusername/davical-klutch.gitgit branch -M mastergit push -u origin masterStep 3: Connect Repository to Klutch.sh
- Navigate to klutch.sh/app
- Click "New Project" and select "Import from GitHub"
- Authorize Klutch.sh to access your GitHub repositories
- Select your DAViCal repository from the list
- Klutch.sh will automatically detect the Dockerfile in your repository
Step 4: Configure Traffic Settings
- In the project settings, select **HTTP** as the traffic type
- Set the internal port to **80**
- 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:postgresPOSTGRES_PASSWORD: Your secure database passwordPOSTGRES_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:
- In your project settings, navigate to the "Storage" section
- Add a volume with mount path: `/var/log/davical` and size: `5GB`
Step 7: Deploy
- Review your configuration settings
- Click "Deploy" to start the deployment process
- Klutch.sh will build your Docker image and deploy the container
- Monitor the build logs for any errors
- 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:
-
Access DAViCal Admin: Navigate to
https://your-app.klutch.sh/ -
First Login: Use the default admin credentials:
- Username:
admin - Password:
admin
- Username:
-
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:
- Log in as admin
- Navigate to Admin → Users
- Click "Create User"
- Fill in user details: - Username: `john` - Full Name: `John Doe` - Email: `john@example.com` - Password: Set a secure password - Active: Check the box
- Click "Create" to save the user
Setting Up Your First Calendar
Each user automatically gets a default calendar, but you can create additional calendars:
- Log in as the user
- Navigate to the calendar view
- Click "Create New Collection"
- Configure calendar properties: - Name: `Work Calendar` - Display Name: `Work` - Description: `Work-related events` - Type: Calendar
- Set permissions if you want to share with others
- Click "Create"
Connecting Calendar Clients
Configure your calendar client to connect to DAViCal:
Apple Calendar (macOS/iOS)
- Open Calendar app (Settings on iOS)
- Go to Preferences → Accounts (or Add Account on iOS)
- Select “Add CalDAV Account”
- 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
- Open Thunderbird
- Right-click on the calendar list → New Calendar
- Select “On the Network”
- Choose “CalDAV”
- Enter location:
https://your-app.klutch.sh/caldav.php/john/calendar/ - When prompted, enter username and password
- Set calendar name and color
Android (via DAVx⁵)
- Install DAVx⁵ from F-Droid or Play Store
- Open DAVx⁵ and add account
- Select “Login with URL and username”
- Enter:
- Base URL:
https://your-app.klutch.sh/caldav.php/john/ - Username:
john - Password: Your password
- Base URL:
- DAVx⁵ will discover calendars and contacts
- Select which collections to sync
- Calendars appear in your device’s calendar app
Evolution (Linux)
- Open Evolution
- Go to File → New → Calendar
- Select “CalDAV”
- Enter:
- URL:
https://your-app.klutch.sh/caldav.php/john/calendar/ - User:
john - Password: Your password
- URL:
- Click “Apply”
Connecting Contact Clients (CardDAV)
For contact synchronization:
Apple Contacts (macOS/iOS)
- System Preferences → Internet Accounts (Settings on iOS)
- Add CardDAV Account
- Enter:
- Server:
your-app.klutch.sh - Username:
john - Password: Your password
- Server:
Thunderbird with CardBook
- Install CardBook add-on
- Right-click in CardBook → New Address Book
- Select “Remote” → “CardDAV”
- 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:
- Create Event: Click a date/time in your calendar app
- Set Details:
- Title: “Team Meeting”
- Location: “Conference Room A”
- Date/Time: Select date and time
- Attendees: Add email addresses
- Save: Event syncs to DAViCal server
- Edit: Changes sync bidirectionally
Sharing Calendars
Share calendars with team members:
- Log into DAViCal web interface as calendar owner
- Navigate to Admin → Calendar Collections
- Select the calendar to share
- Click "Privileges" tab
- Add principal (user or group)
- Set permissions: - **Read**: View events - **Read Free/Busy**: See only free/busy status - **Write**: Create and edit events - **All**: Full control
- 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:
- From Web Interface: Navigate to “Free/Busy Lookup”
- Enter email or username of person to check
- Select date range
- View availability displayed graphically
Calendar clients typically handle this automatically when inviting attendees.
Production Best Practices
Security Hardening
-
Use Strong Passwords: Enforce password policies for all users
-
Update Default Credentials: Change all default passwords immediately after deployment
-
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>- Restrict Admin Access: Limit admin interface to specific IPs if possible:
// In config.php$c->restrict_setup_to_admin_ip = ['your.admin.ip.address'];- Database Security: Use strong PostgreSQL passwords and restrict connections:
-- In PostgreSQLALTER USER davical_app WITH PASSWORD 'very-strong-random-password';ALTER USER davical_dba WITH PASSWORD 'another-strong-password';- 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
- PostgreSQL Configuration: Optimize PostgreSQL settings for your workload
Create postgresql.conf customizations:
# Memory settingsshared_buffers = 256MBeffective_cache_size = 1GBwork_mem = 16MBmaintenance_work_mem = 128MB
# Connection settingsmax_connections = 100
# Write-ahead logwal_buffers = 16MBcheckpoint_completion_target = 0.9
# Query tuningrandom_page_cost = 1.1effective_io_concurrency = 200- Regular Maintenance: Schedule database maintenance:
#!/bin/bashPGPASSWORD=$POSTGRES_PASSWORD psql -h $POSTGRES_HOST -U $POSTGRES_USER -d davical <<EOFVACUUM ANALYZE;REINDEX DATABASE davical;EOFRun weekly via cron or scheduled task.
- Connection Pooling: For high-traffic deployments, use PgBouncer for connection pooling
Backup Strategy
Implement comprehensive backups:
- Database Backups: Regular PostgreSQL backups:
#!/bin/bashBACKUP_DIR="/backups/davical_$(date +%Y%m%d_%H%M%S)"mkdir -p $BACKUP_DIR
# Backup databasePGPASSWORD=$POSTGRES_PASSWORD pg_dump -h $POSTGRES_HOST -U $POSTGRES_USER \ -d davical -F c -f $BACKUP_DIR/davical.dump
# Backup configurationtar -czf $BACKUP_DIR/config.tar.gz /etc/davical/config.php
# Keep only last 30 daysfind /backups -name "davical_*.dump" -mtime +30 -deletefind /backups -name "config.tar.gz" -mtime +30 -delete- Automated Backups: Schedule daily backups:
# Add to crontab0 2 * * * /usr/local/bin/backup-davical.sh- Test Restores: Regularly test backup restoration:
# Restore from backupPGPASSWORD=$POSTGRES_PASSWORD pg_restore -h $POSTGRES_HOST -U $POSTGRES_USER \ -d davical -c /backups/davical_20231201_020000/davical.dumpPerformance Optimization
- 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>- PHP Optimization: Tune PHP settings:
; In php.ini or .user.inimemory_limit = 256Mmax_execution_time = 60upload_max_filesize = 10Mpost_max_size = 10M
; OPcache settingsopcache.enable=1opcache.memory_consumption=128opcache.interned_strings_buffer=8opcache.max_accelerated_files=4000opcache.revalidate_freq=60- Database Indexes: Ensure proper indexes exist:
-- Check and create indexesCREATE 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);- Caching: Enable opcache and query result caching
Monitoring and Logging
- Apache Logs: Monitor access and error logs:
# View real-time logstail -f /var/log/davical/access.logtail -f /var/log/davical/error.log
# Analyze common errorsgrep -i "error" /var/log/davical/error.log | tail -20- Database Monitoring: Track database performance:
-- View active connectionsSELECT count(*) FROM pg_stat_activity WHERE datname = 'davical';
-- View slow queriesSELECT pid, now() - pg_stat_activity.query_start AS duration, queryFROM pg_stat_activityWHERE state = 'active' AND now() - pg_stat_activity.query_start > interval '5 seconds';
-- View table sizesSELECT relname, pg_size_pretty(pg_total_relation_size(relid))FROM pg_catalog.pg_statio_user_tablesORDER BY pg_total_relation_size(relid) DESC;- Health Check Endpoint: Monitor DAViCal availability:
# Simple health checkcurl -f https://your-app.klutch.sh/ || echo "DAViCal is down!"
# CalDAV-specific checkcurl -X PROPFIND -u john:password https://your-app.klutch.sh/caldav.php/john/ \ -H "Depth: 0" -H "Content-Type: application/xml"- Log Rotation: Implement log rotation to manage disk space:
/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:
-
Verify URL Format: Ensure correct CalDAV URL:
https://your-app.klutch.sh/caldav.php/username/ -
Check Credentials: Verify username and password are correct
-
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" -
Check Apache Logs:
Terminal window tail -f /var/log/davical/error.log -
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:
-
Verify User Exists: Check database:
SELECT username, active FROM usr WHERE username = 'john'; -
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 -
Check LDAP Configuration (if using LDAP):
Terminal window # Test LDAP bindldapsearch -x -H ldap://ldap.example.com -D "uid=john,ou=users,dc=example,dc=com" -W -
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:
-
Check Sync Interval: Verify client sync settings (should be automatic or every 5-15 minutes)
-
Force Refresh: In calendar client, right-click calendar and select “Refresh”
-
Verify Collection Path: Ensure all clients use the same calendar URL
-
Check Database Updates:
SELECT caldav_data, modified FROM caldav_dataWHERE user_no = (SELECT user_no FROM usr WHERE username = 'john')ORDER BY modified DESC LIMIT 10; -
Clear Client Cache: Remove and re-add calendar in client
Database Connection Errors
Symptoms: “Failed to connect to database” or PostgreSQL connection errors
Solutions:
-
Verify PostgreSQL is Running:
Terminal window pg_isready -h $POSTGRES_HOST -p 8000 -
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'; -
Test Direct Connection:
Terminal window PGPASSWORD=your_password psql -h postgres-app.klutch.sh -p 8000 -U davical_app -d davical -c "SELECT 1;" -
Check PostgreSQL Logs: Review database logs for connection issues
-
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:
-
Check RRULE Format: Verify iCalendar RRULE is valid:
SELECT caldav_data FROM caldav_data WHERE caldav_data LIKE '%RRULE%' LIMIT 1; -
Update Timezone Data: Ensure timezone definitions are current:
Terminal window # Update timezone data in DAViCalwget https://www.tzurl.org/zoneinfo-outlook/timezone-defs.ics# Import to DAViCal -
Client Compatibility: Some clients have issues with complex RRULEs; test with multiple clients
-
Check Date Range: Ensure client is querying appropriate date range for recurrence
Performance Issues
Symptoms: Slow response times, calendar takes long to load
Solutions:
-
Check Database Performance:
-- View slow queriesSELECT query, mean_exec_time, callsFROM pg_stat_statementsORDER BY mean_exec_time DESCLIMIT 10; -
Optimize Large Calendars: Archive old events:
-- Delete events older than 2 yearsDELETE FROM caldav_dataWHERE caldav_type = 'VEVENT'AND modified < NOW() - INTERVAL '2 years'; -
Reduce Client Sync Range: Configure clients to sync only last 6-12 months
-
Check Resource Usage:
Terminal window # Monitor CPU and memorytop# Check disk I/Oiostat -x 1 -
Scale Resources: Increase CPU/memory allocation in Klutch.sh dashboard
Advanced Configuration
LDAP Authentication Integration
Integrate with existing LDAP directory:
- 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'];- Install PHP LDAP Extension:
# Add to DockerfileRUN apt-get install -y php-ldap- Test LDAP Integration:
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
-
Custom Domain: Configure your domain in Klutch.sh dashboard
-
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";- Custom Logo: Replace default logo:
# Copy your logocp your-logo.png /usr/share/davical/htdocs/images/logo.png- Custom CSS: Create custom stylesheet:
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.cssCalendar Delegation
Allow users to delegate calendar access:
- Enable in Configuration:
$c->enable_delegation = true;-
Set Delegates via Web Interface:
- User logs in
- Navigates to “Relationships”
- Adds delegate with permissions
-
Calendar URL for Delegates:
https://your-app.klutch.sh/caldav.php/john/?delegate=secretary
Resource Booking (Conference Rooms)
Set up bookable resources:
- 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 resourceINSERT INTO resource (user_no, resource_type)VALUES ((SELECT user_no FROM usr WHERE username = 'conf-room-a'), 'room');- Configure Resource Properties:
UPDATE usr SET schedule_transp = 'opaque' WHERE username = 'conf-room-a';-
Set Booking Permissions: Allow users to schedule resources
-
Access Resource Calendar:
https://your-app.klutch.sh/caldav.php/conf-room-a/
Email Notifications
Enable email notifications for calendar invitations:
- 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';- Enable Scheduling:
$c->enable_scheduling = true;$c->scheduling_notify = true;- Test Email:
# Send test email via PHPphp -r "mail('test@example.com', 'Test', 'Test message');"Timezone Management
Maintain accurate timezone data:
- Update Timezone Database:
#!/bin/bashcd /tmpwget https://www.tzurl.org/zoneinfo-outlook/aliases.txtwget https://www.tzurl.org/zoneinfo-outlook/timezone-defs.ics
# Import to DAViCalPGPASSWORD=$POSTGRES_PASSWORD psql -h $POSTGRES_HOST -U davical_app -d davical <<EOF-- SQL to import timezone dataEOF- Configure Timezone Server:
$c->timezone_server = "https://www.tzurl.org/zoneinfo-outlook/";API Access and Automation
Automate DAViCal operations:
- Create Events via curl:
#!/bin/bashUSERNAME="john"PASSWORD="password"CALENDAR_URL="https://your-app.klutch.sh/caldav.php/john/calendar/"
# iCalendar event dataEVENT_DATA='BEGIN:VCALENDARVERSION:2.0PRODID:-//Example Corp//CalDAV Client//ENBEGIN:VEVENTUID:20231201T120000-john@example.comDTSTAMP:20231201T120000ZDTSTART:20231215T140000ZDTEND:20231215T150000ZSUMMARY:Team MeetingDESCRIPTION:Weekly team syncLOCATION:Conference RoomEND:VEVENTEND:VCALENDAR'
# Create eventcurl -X PUT -u "$USERNAME:$PASSWORD" \ -H "Content-Type: text/calendar" \ --data "$EVENT_DATA" \ "$CALENDAR_URL/20231201T120000-john@example.com.ics"- Query Events:
# Fetch all eventscurl -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/- Python Script Example:
#!/usr/bin/env python3import caldavfrom datetime import datetime, timedelta
# Connect to DAViCalclient = caldav.DAVClient( url="https://your-app.klutch.sh/caldav.php/john/", username="john", password="password")
principal = client.principal()calendars = principal.calendars()
# Create eventcal = 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 eventsevents = 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:
-
Create Separate Databases: One database per tenant
-
Use Subdirectories: Different paths for each tenant:
https://your-app.klutch.sh/tenant1/https://your-app.klutch.sh/tenant2/
-
Apache Virtual Hosts: Configure multiple virtual hosts
-
Separate Config Files: Each tenant has own
config.php
Additional Resources
- Official DAViCal Website
- DAViCal Wiki
- DAViCal GitLab Repository
- CalDAV RFC 4791
- CardDAV RFC 6352
- Klutch.sh Documentation
- Persistent Storage Guide
- Custom Domain Configuration
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.