Skip to content

Easy!Appointments

Easy!Appointments lets customers book time slots with a provider. Two containers — PHP with Apache, and MariaDB. No build step and no Node.js.

Last run on a host 9 Nov 2026 · data restored from a backup

Pinned at 1.6.1 (released 19 Oct 2026) · the newest upstream release

In the upstream images: 3 CRITICAL · 80 HIGH with a published fix — per image

To try it: Docker, nothing else. See Try it locally.

To deploy it behind the proxy: Traefik running and a domain.

On your own machine, with no proxy, domain or certificate. From apps/easyappointments/:

Terminal window
cp .env.local.example .env.local
# The secrets ship empty; generate the ones this stack needs
{
echo "DB_PASSWORD=$(openssl rand -base64 32 | tr -d '\n')"
echo "DB_ROOT_PASSWORD=$(openssl rand -base64 32 | tr -d '\n')"
} >> .env.local
mkdir -p volumes/local/mysql volumes/local/storage
sudo chown -R 33:33 volumes/local/storage
docker compose -f docker-compose.local.yml --env-file .env.local up -d

Then open http://localhost:8080. Stop it again with the same command and down.

From apps/easyappointments/:

  1. Copy the environment file and set APP_TRAEFIK_HOST and TZ:

    Terminal window
    cp .env.example .env
  2. Create the directories and the secrets, and mirror the password into .env:

    Terminal window
    mkdir -p .secrets volumes/mysql volumes/storage
    openssl rand -base64 32 | tr -d '\n' > .secrets/db_pwd.txt
    openssl rand -base64 32 | tr -d '\n' > .secrets/db_root_pwd.txt
    sed -i "s|^DB_PWD_INLINE=.*|DB_PWD_INLINE=$(cat .secrets/db_pwd.txt)|" .env
  3. Hand the storage directory to the user Apache runs as, uid 33. Sessions are written there, and without write access no login can complete:

    Terminal window
    sudo chown -R 33:33 volumes/storage
  4. Start it:

    Terminal window
    docker compose up -d
    docker compose logs easyappointments-app --follow

    Wait for apache2 -D FOREGROUND.

  5. Open https://<APP_TRAEFIK_HOST>/index.php/installation from the VPN — the route ships access-vpn until the wizard has run, because the wizard creates the administrator account for whoever reaches it first. Afterwards log in at /index.php/user/login, then set APP_TRAEFIK_ACCESS=access-public in .env and docker compose up -d if the booking page is to be public.

Terminal window
docker compose ps # two services, healthy

Then log in, create one service and one provider, and book an appointment from a private browser window. Logging in successfully is what proves step 3 worked.

Both book appointments. This one is PHP and MariaDB with a long track record and a small footprint; Cal.diY is a Next.js application with routing forms, team scheduling and workflows — and a heavier stack. If the need is one-to-one booking on a host with a modest memory budget, this is the simpler answer.

The compose file passes no mail settings. Booking notifications are configured in the interface — Admin → Settings → Business → Email — and the SMTP host and credentials live in the database, not in .env. Until they are set, nobody is told about a booking.

Database MariaDB, container easyappointments-db, database easyappointments, user easyappointments
Password .secrets/db_pwd.txt
State volumes/mysql (database) · volumes/storage (uploads, generated files)
Quiescing Not needed — the dump is consistent on its own.

For borgmatic:

mariadb_databases:
- name: easyappointments
container: easyappointments-db
username: easyappointments
password: "{credential file /srv/docker/apps/easyappointments/.secrets/db_pwd.txt}"

Appointments are database rows, so an older database comes back without the bookings made since — and the people who made them still expect to be seen. Reconcile against the notification mail before resuming.

The database and volumes/storage come back from the same backup run, so generated files match the rows that reference them.

Restore order: database, then the application.

  1. Read the release notes.

  2. Back up the database and volumes/storage together.

  3. Raise APP_TAG in .env, then:

    Terminal window
    docker compose pull && docker compose up -d
  4. Log in and open the calendar. A schema change surfaces there.

Login returns to the login page with no error. Sessions cannot be written. volumes/storage is not owned by uid 33 — this is the failure step 3 prevents.

The install wizard appears again after setup. The database is empty or unreachable. Check docker compose logs db.