Self-hosting

All docs

Qualendar 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.