WebCalendar

How to Install WebCalendar with Docker Compose (Step-by-Step Guide)

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 uses a single, self-contained docker-compose.yml built on the published Docker image, with a persistent MariaDB volume and configuration in environment variables. You don’t need to clone the source code or install PHP on the host, and there’s no config file to manage.

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 version should print v2.x). On older installs the command is the hyphenated docker-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.


Setting up WebCalendar

Create an empty directory for your calendar. Everything below happens inside it.

1. 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
    restart: unless-stopped

  webcalendar:
    image: craigk5n/webcalendar:1.9.24
    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, so there’s no writable config file inside the container. The image is pinned to a version tag (1.9.24) so an unexpected latest update never surprises you in production. The current tags are on Docker Hub, and every image is built for both amd64 and arm64.

2. Start the containers:

docker compose up -d

3. Create the tables and your admin account. Give MariaDB a few seconds to initialize on first boot, then run WebCalendar’s headless installer inside the container:

docker compose exec webcalendar php wizard/headless.php --use-env \
  --admin-login=admin --admin-password='choose-a-strong-password'

The installer creates the database tables, creates the admin account with a properly hashed password, and ends with Installation Complete!. WebCalendar has no default admin/admin login; recent versions removed it as a security risk, so the account you create here is the only way in. The password appears in your shell history. If that bothers you, run docker compose exec webcalendar php bin/webcal.php user reset-password --login=admin afterward, which generates a new password and prints it once.

4. Log in. Browse to http://localhost:8080 and sign in with the account you just created.

To watch the logs or stop the stack:

docker compose logs -f
docker compose down      # stop (keeps data)

The MariaDB data lives in the named Docker volume db-data, so down and up won’t lose your events. Only down -v deletes the volume.

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

This setup exposes 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

Take a database backup first (see above), every time. Then bump the image tag, pull, recreate the app container, and run the headless installer again to apply any database changes:

# edit docker-compose.yml: craigk5n/webcalendar:1.9.23 -> :1.9.24
docker compose pull webcalendar
docker compose up -d webcalendar
docker compose exec webcalendar php wizard/headless.php --use-env

Upgrades aren’t applied automatically. Until the installer runs, every page redirects to the setup wizard. On an existing database the installer detects the old version, upgrades the tables, and leaves your admin account alone. To check whether an upgrade is pending without changing anything, run docker compose exec webcalendar php bin/webcal.php db check. It exits with 0 if the database is up to date and 1 if an upgrade is pending. Read the release notes before each upgrade for any breaking changes.

Troubleshooting

  • Headless installer can’t connect, or a blank page: the app started before MariaDB finished initializing. Wait a few seconds and try again, or run docker compose restart webcalendar. Confirm WEBCALENDAR_DB_PASSWORD exactly matches MARIADB_PASSWORD.
  • Every page redirects to the install wizard: the database hasn’t been set up or upgraded yet. Run the headless installer (step 3 above for a new install, or the last command under Upgrading).
  • Locked out of the admin account: run docker compose exec webcalendar php bin/webcal.php user reset-password --login=admin. It generates a new password and prints it once. Passwords are stored as bcrypt hashes, so you can’t fix a login by editing the database by hand.
  • Port 8080 already in use: change the host side of the mapping, e.g. - "9090:80".
  • Check the logs: docker compose logs webcalendar and docker compose logs db are your first stop for anything. For a bug report, docker compose exec webcalendar php bin/webcal.php diagnose prints an environment summary with no passwords or secrets in it.

Next steps

Running WebCalendar in Docker in production? I’d love to hear how — leave a comment with your setup.

Leave a Reply

Your email address will not be published. Required fields are marked *