Self-hosting
All docsQualendar is free software, so you can run your own instance. It ships with a
production Docker image (docker/prod/Dockerfile) that bundles nginx
and PHP-FPM in a single container and runs database migrations on start-up.
Requirements
- Docker and Docker Compose
- A PostgreSQL database (the example below runs one in a container)
Running Qualendar
Clone the repository:
git clone https://github.com/ubermuda/qualendar.git
cd qualendar
Add a Compose file — e.g. compose.prod.yml — with two services:
the app image and a PostgreSQL database. This one has none of the
development-only Traefik wiring, and publishes the app on a plain port for
your own reverse proxy to sit in front of:
services:
app:
build:
context: .
dockerfile: docker/prod/Dockerfile
restart: unless-stopped
ports:
- "8080:80" # front this with your own TLS-terminating proxy
environment:
APP_SECRET: "replace-with-a-random-secret"
DATABASE_URL: "postgresql://app:strong-password@database:5432/app?serverVersion=16&charset=utf8"
MAILER_DSN: "smtp://user:[email protected]:587"
depends_on:
database:
condition: service_healthy
database:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: "strong-password"
healthcheck:
test: ["CMD", "pg_isready", "-U", "app", "-d", "app"]
timeout: 5s
retries: 5
start_period: 60s
volumes:
- database_data:/var/lib/postgresql/data
volumes:
database_data:
Build and start it:
docker compose -f compose.prod.yml up -d --build
The app container runs doctrine:migrations:migrate automatically
on boot, so there is no separate migration step. It serves the web app and
CalDAV over plain HTTP on port 80 (published as 8080 above).
Configuration
Set these before the first boot:
APP_SECRET— a random string (e.g.openssl rand -hex 16).DATABASE_URL— must match the database service's credentials.MAILER_DSN— an SMTP endpoint for transactional email (verification, invitations). Email is sent synchronously, so no queue worker is required.POSTGRES_PASSWORD— change it from the placeholder above.
Hostname and TLS
The app image terminates no TLS — it serves plain HTTP on port 80. Put a reverse proxy that terminates TLS (Traefik, Caddy, nginx, …) in front of the published port and route your hostname to it. A public ACME certificate authority such as Let's Encrypt is the usual choice for a real domain.
The maintainer's local development setup instead uses a Traefik +
step-ca proxy to issue trusted *.dev.localhost certificates —
see the Contributing page.
Your obligations under the AGPL
Qualendar is licensed under the GNU Affero General Public License v3.0 or later. Because it is the AGPL, running it as a network service counts as distribution: if you run a modified version and let other people use it over a network, you must offer those users the complete corresponding source of your modified version. Running an unmodified copy for your own household carries no such obligation.
See the Open source page for a plain-English summary of the license.