WebCalendar is a free, open-source calendar application written in PHP. You can run it as a personal calendar, a shared calendar for a group or intranet, or a public event calendar. The fastest and cleanest way to get it running today is with Docker Compose — no fiddling with PHP versions, Apache modules, or database packages on your host.
This guide covers two paths:
- Quick start — clone the repo and bring the stack up with a single command. Best for trying WebCalendar or spinning up a dev instance.
- Production setup — a self-contained
docker-compose.ymlusing the published Docker image, a persistent MariaDB volume, and environment-based configuration. Best for a server you’ll actually keep.
By the end you’ll have WebCalendar running at http://localhost:8080 with your data safely persisted.
Prerequisites
- Docker Engine and the Docker Compose plugin (
docker compose versionshould print v2.x). On older installs the command is the hyphenateddocker-compose— both work here. - About 5 minutes.
- A machine with a couple hundred MB of free RAM. WebCalendar is light.
WebCalendar 1.9.x requires PHP 8.2+ and a supported database (MySQL/MariaDB, PostgreSQL, or SQLite3). The container images bundle the right PHP for you, so you don’t need PHP installed on the host.
Method 1 — Quick start (clone + official Compose file)
The repository ships with a ready-to-run Compose file. This is the path documented in the project README, and it automatically loads the database schema for you.
git clone https://github.com/craigk5n/webcalendar.git
cd webcalendar
docker compose -f docker/docker-compose-php8.yml up -d
That’s it. Three things just happened:
- A MariaDB container (
webcalendar-db) started and, on first boot, loaded the WebCalendar schema frominstall/sql/tables-mysql.sql. - A PHP 8 + Apache container started serving WebCalendar.
- Port 8080 on your host was mapped to Apache’s port 80 in the container.
Open http://localhost:8080 in your browser. On first run WebCalendar’s setup wizard writes its configuration to includes/settings.php, then hands you off to the login page.
Log in with the default admin account —
admin/admin— and change the password immediately under Preferences (or the admin account’s settings). This is the single most important step; a self-hosted calendar left on the default password is an open door.
To watch the logs or stop the stack:
docker compose -f docker/docker-compose-php8.yml logs -f
docker compose -f docker/docker-compose-php8.yml down # stop (keeps data)
The MariaDB data lives in a named Docker volume (mysql-data), so down and up won’t lose your events. Only down -v deletes the volume.
Method 2 — Production setup (published image, no clone)
For a server you intend to keep, you don’t need the source tree at all. Pull the published image, k5nus/webcalendar, and configure everything through environment variables so there’s no interactive wizard and no writable settings.php to manage.
Create an empty directory and two files.
1. Grab the schema (loaded into MariaDB on first boot):
curl -O https://raw.githubusercontent.com/craigk5n/webcalendar/master/install/sql/tables-mysql.sql
2. Create docker-compose.yml:
services:
db:
image: mariadb:11
container_name: webcalendar-db
environment:
MARIADB_DATABASE: webcalendar
MARIADB_USER: webcalendar
MARIADB_PASSWORD: change-me-to-a-strong-password
MARIADB_ROOT_PASSWORD: change-me-too
volumes:
- db-data:/var/lib/mysql
- ./tables-mysql.sql:/docker-entrypoint-initdb.d/01-tables.sql:ro
restart: unless-stopped
webcalendar:
image: k5nus/webcalendar:latest # or pin a version, e.g. k5nus/webcalendar:1.9.16
container_name: webcalendar
depends_on:
- db
ports:
- "8080:80"
environment:
WEBCALENDAR_USE_ENV: "true"
WEBCALENDAR_DB_TYPE: mysqli
WEBCALENDAR_DB_HOST: db
WEBCALENDAR_DB_DATABASE: webcalendar
WEBCALENDAR_DB_LOGIN: webcalendar
WEBCALENDAR_DB_PASSWORD: change-me-to-a-strong-password # must match MARIADB_PASSWORD
WEBCALENDAR_DB_PERSISTENT: "true"
WEBCALENDAR_MODE: prod
restart: unless-stopped
volumes:
db-data:
When WEBCALENDAR_USE_ENV is true, WebCalendar reads its database connection from these variables and ignores includes/settings.php entirely — which means no writable config file inside the container and no wizard step. Pin the image to a version tag (like 1.9.16) in production so an unexpected latest update never surprises you.
3. Bring it up:
docker compose up -d
Browse to http://localhost:8080 and log in as admin / admin, then change the password.
Optionally protecting the installer
WebCalendar can guard its setup/settings routines behind an install password. Generate an MD5 hash of your chosen password and pass it in:
php -r "echo md5('YourInstallPassword');"
Add the result as WEBCALENDAR_INSTALL_PASSWORD to the webcalendar service’s environment.
Where your data lives (and how to back it up)
Everything that matters — users, events, categories, preferences — is in the database, which is stored in the db-data Docker volume. Backing WebCalendar up is therefore just a MariaDB dump:
# Backup
docker compose exec db
mariadb-dump -u webcalendar -p'change-me-to-a-strong-password' webcalendar
> webcalendar-backup-$(date +%F).sql
# Restore
docker compose exec -T db
mariadb -u webcalendar -p'change-me-to-a-strong-password' webcalendar
< webcalendar-backup-2026-07-08.sql
Store those dumps off the server (S3, another host, etc.). Automate it with a nightly cron job and you have a real disaster-recovery story.
Putting it behind a domain with HTTPS
Method 1 and 2 both expose plain HTTP on port 8080 — fine for localhost, not for the public internet. In production you’ll want a reverse proxy (Nginx, Caddy, or Traefik) terminating TLS in front of the container and forwarding to webcalendar:80. That’s a post of its own — Run WebCalendar Behind a Reverse Proxy with HTTPS — but the short version: don’t publish port 8080 to the world; bind it to 127.0.0.1:8080 and let your proxy handle 443.
Upgrading
With a pinned image tag, upgrading is: bump the tag, pull, and recreate the app container.
# edit docker-compose.yml: k5nus/webcalendar:1.9.16 -> :1.9.17
docker compose pull webcalendar
docker compose up -d webcalendar
WebCalendar applies any needed database updates on the next load. Take a database backup first (see above) — always, every time.
Troubleshooting
- Blank page or “cannot connect to database”: the app started before MariaDB finished initializing. Give it a few seconds and reload, or
docker compose restart webcalendar. ConfirmWEBCALENDAR_DB_PASSWORDexactly matchesMARIADB_PASSWORD. - Login fails with admin/admin: the schema didn’t load. In Method 2, make sure
tables-mysql.sqlwas present before the firstup— the init script only runs on an empty data volume. Rundocker compose down -v && docker compose up -dto re-initialize (this wipes data). - Port 8080 already in use: change the host side of the mapping, e.g.
- "9090:80". - Check the logs:
docker compose logs webcalendaranddocker compose logs dbare your first stop for anything.
Next steps
- Download options and release notes: WebCalendar Downloads
- Not sure WebCalendar fits your use case? Compare it with the best self-hosted Google Calendar alternatives.
- Source and issues: craigk5n/webcalendar on GitHub
- Docker images: k5nus/webcalendar on Docker Hub
Running WebCalendar in Docker in production? I’d love to hear how — leave a comment with your setup.
Leave a Reply