Applications AWS

LibreBooking 7.2.0 on AWS User Guide

| Product: LibreBooking 7.2.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the LibreBooking 7.2.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg documents, forms and scheduling listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

LibreBooking is an open source, self hosted scheduling and reservation application, the maintained community fork of phpScheduleIt and Booked. Your team books meeting rooms, desks, vehicles, lab equipment or any other shared resource on a calendar. You set up schedules and resources, decide who may book what, require approval where you need it, and see everything on the dashboard and the schedule. Everything stays on your own instance in your own VPC.

A fresh upstream install starts with a web installer that anyone who reaches it can run, lets visitors register their own accounts, and leaves its REST API on. The cloudimg image changes that:

  • HTTPS from the first boot. nginx serves LibreBooking on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS. The certificate's SHA-256 fingerprint is written to the credentials file so you can check it the first time you connect.
  • One administrator, unique to your instance. First boot creates the database, its password and a single Application Administrator called admin-<6 hex characters> with a random password, using LibreBooking's own installer. There are no sample accounts and no web installer on the image. The account name and password are written to /root/librebooking-credentials.txt (root only).
  • Closed by default. Self registration, the anonymous schedule view, password reset by e-mail, all e-mail, the REST API, calendar subscriptions, reservation attachments and the in browser settings page are off. You add people yourself, and you turn e-mail on when you have an SMTP relay (Step 9).
  • Fail closed. nginx and PHP-FPM refuse to start until first boot has generated the certificate, the database and the administrator, so the application is never served half configured.
  • Hardened. Requests for host names the instance does not serve are dropped, sign-in attempts are rate limited, cross site posts to sign-out are refused, LibreBooking's code is owned by root and read only to PHP, PHP never runs from the upload directory, MySQL listens on a unix socket only with no binary log, nginx, PHP-FPM and MySQL are blocked from the instance metadata service, the PHP user cannot reach private addresses in your VPC unless you allow them (Step 10), and the AMI requires IMDSv2.
  • The official release, verified. The LibreBooking 7.2.0 release is checked against a pinned SHA-256 checksum and its PHP dependencies are installed from upstream's own composer.lock, with one security update: symfony/html-sanitizer is raised from 7.4.13 to 7.4.23 to fix CVE-2026-118517, CVE-2026-121373 and CVE-2026-121949. The image is fully patched Ubuntu 24.04 LTS with unattended security upgrades on.

What is on the image: LibreBooking 7.2.0 in /var/www/librebooking, PHP 8.3 (PHP-FPM), MySQL 8.0 and nginx from the Ubuntu archive, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
  • A security group that allows TCP 22 (SSH), TCP 443 (LibreBooking) and TCP 80 (redirects to HTTPS). Open 22 from your own networks only; open 443 and 80 to the people who will book resources.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick LibreBooking 7.2.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above.

With the AWS CLI (replace the values in angle brackets):

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=librebooking}]'

Step 2: Connect to your instance

Version SSH user
LibreBooking 7.2.0 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot creates the database, the administrator and the certificate, then nginx and PHP-FPM start. librebooking-firstboot runs once and then shows as inactive:

systemctl is-active nginx php8.3-fpm mysql librebooking-egress-guard librebooking-firstboot
sudo test -f /var/lib/cloudimg/librebooking-firstboot.done && echo FIRSTBOOT_DONE
active
active
active
active
inactive
FIRSTBOOT_DONE

Only SSH and nginx listen on the network. MySQL has no TCP listener at all, and PHP-FPM listens on a unix socket:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | sed -E 's/pid=[0-9]+,fd=[0-9]+/.../g' | sort
0.0.0.0:22 users:(("sshd",...),("systemd",...))
0.0.0.0:443 users:(("nginx",...),("nginx",...),("nginx",...))
0.0.0.0:80 users:(("nginx",...),("nginx",...),("nginx",...))
127.0.0.53%lo:53 users:(("systemd-resolve",...))
127.0.0.54:53 users:(("systemd-resolve",...))
[::]:22 users:(("sshd",...),("systemd",...))
[::]:443 users:(("nginx",...),("nginx",...),("nginx",...))
[::]:80 users:(("nginx",...),("nginx",...),("nginx",...))

Step 4: Get your URL and administrator password

The credentials file is readable by root only. This prints everything in it except the password line:

sudo grep -v '^LIBREBOOKING_ADMIN_PASSWORD=' /root/librebooking-credentials.txt
# LibreBooking 7.2.0 - generated on first boot by librebooking-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Keep this file secret.

LIBREBOOKING_URL=<LIBREBOOKING_URL>
LIBREBOOKING_ADMIN_USER=admin-<6hex>
LIBREBOOKING_TLS_SHA256=<LIBREBOOKING_TLS_SHA256>
# Open LIBREBOOKING_URL in a browser ...
...

Print the administrator password itself only when you are ready to sign in: sudo grep '^LIBREBOOKING_ADMIN_PASSWORD=' /root/librebooking-credentials.txt | cut -d= -f2-

<LIBREBOOKING_URL> is https://<the instance's public DNS name>/Web/, taken from EC2 instance metadata on first boot. The rest of the file lists what is switched off, where the database password is kept and the helper commands used later in this guide. To serve LibreBooking under your own DNS name, see Step 11.

Step 5: Check the certificate and sign in

The certificate is self signed, so your browser asks you to confirm it the first time. Before you accept it, compare its SHA-256 fingerprint (in the browser's certificate viewer) with <LIBREBOOKING_TLS_SHA256> from the credentials file. From your workstation you can read it with OpenSSL:

echo | openssl s_client -connect <public-ip>:443 2>/dev/null | openssl x509 -noout -fingerprint -sha256

On the instance itself the same check reads the fingerprint nginx serves:

H=$(sudo sed -n 's#^LIBREBOOKING_URL=https://\([^/]*\)/.*#\1#p' /root/librebooking-credentials.txt)
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -fingerprint -sha256
sudo grep '^LIBREBOOKING_TLS_SHA256=' /root/librebooking-credentials.txt

The two fingerprints are the same:

sha256 Fingerprint=<LIBREBOOKING_TLS_SHA256>
LIBREBOOKING_TLS_SHA256=<LIBREBOOKING_TLS_SHA256>

Open <LIBREBOOKING_URL> and sign in with LIBREBOOKING_ADMIN_USER (or its e-mail address, admin-<6hex>@example.com) and LIBREBOOKING_ADMIN_PASSWORD. There is no Register link, because self registration is off:

The LibreBooking sign-in page with Email or Username and Password fields and a Sign In button, and no registration link

First, open My Account > Profile and give the administrator your real e-mail address, name and organisation, so the account is recognisable in reservations and, once e-mail is on, receives notifications.

Step 6: Set up your resources

Under Application Management > Resources select Add Resource for each thing people will book: rooms, desks, vehicles or equipment. Each resource belongs to a schedule (the image starts with LibreBooking's default schedule) and can have a location, a contact, a capacity, booking rules such as minimum and maximum length and notice, and an approval requirement:

The LibreBooking Resources management page listing Meeting Room A, Meeting Room B and a Projector Kit, with the Add Resource button and filters for schedule, type, status and approval

Application Management > Schedules sets the opening hours and slot layout for each schedule. Application Management > Users is where you add people, since they cannot register themselves; Groups lets you give a team permission to book a resource or approve its reservations.

Step 7: Book a resource

Schedule > Bookings shows the week for each schedule, with existing reservations in place. Select a free slot to book it:

The LibreBooking week schedule showing Meeting Room A, Meeting Room B and the Projector Kit with several reservations across the week

In the reservation form set the title, the resource, the start and end, any repeat, participants and invitees, then select Create:

The LibreBooking new reservation form for a Customer workshop in Meeting Room B, with the owner, begin and end times and the resource filled in

The Dashboard lists each person's upcoming reservations and announcements:

The LibreBooking dashboard listing five upcoming reservations for the Facilities Team

Step 8: Check the security posture from the command line

Port 80 redirects to HTTPS, the site root redirects to the sign-in page, and the registration page, the web installer, the REST API and the configuration are not served. A request for a host name the instance does not serve is dropped without an answer:

H=$(sudo sed -n 's#^LIBREBOOKING_URL=https://\([^/]*\)/.*#\1#p' /root/librebooking-credentials.txt)
curl -s -o /dev/null -w 'http://          -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'https:// /      -> %{http_code} %{redirect_url}\n' --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'sign-in page    -> %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/Web/"
curl -sk -o /dev/null -w 'register.php    -> %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/Web/register.php"
curl -sk -o /dev/null -w 'web installer   -> %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/Web/install/"
curl -sk -o /dev/null -w 'REST API        -> %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/Web/Services/"
curl -sk -o /dev/null -w 'config.php      -> %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/config/config.php"
curl -sk -o /dev/null -w 'unknown host    -> %{http_code}\n' --resolve "evil.example:443:127.0.0.1" "https://evil.example/" || true
http://          -> 301 https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/
https:// /      -> 301 https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/Web/
sign-in page    -> 200
register.php    -> 404
web installer   -> 404
REST API        -> 404
config.php      -> 404
unknown host    -> 000

000 means nginx closed the connection without answering, which is how it treats any host name it does not serve.

The image ships a round trip check that signs in through LibreBooking's own sign-in form with the administrator's password, and proves that a wrong password and the upstream sample accounts are refused. It never prints the password:

sudo librebooking-cred-roundtrip
LIBREBOOKING_ROUNDTRIP_OK https 301 + / -> /Web/ + no code/config/installer/API/registration/upload PHP + health + administrator sign-in (LibreBooking's own sign-in form) + wrong password and sample accounts refused

The secrets live in root only or group restricted files. config.php holds the database password and is readable by the librebooking group (PHP-FPM) but writable by root only:

sudo stat -c '%a %U:%G %n' /root/librebooking-credentials.txt /etc/librebooking/config.php /etc/nginx/ssl/librebooking.key /etc/nginx/ssl/librebooking.crt
600 root:root /root/librebooking-credentials.txt
640 root:librebooking /etc/librebooking/config.php
600 root:root /etc/nginx/ssl/librebooking.key
644 root:root /etc/nginx/ssl/librebooking.crt

nginx, PHP-FPM and MySQL cannot reach the instance metadata service:

systemctl show nginx php8.3-fpm mysql -p Id -p IPAddressDeny
IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32
Id=nginx.service

IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32
Id=php8.3-fpm.service

IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32
Id=mysql.service

The closed defaults are in /etc/librebooking/config.php. The three lines are self registration, password reset (disabled) and the anonymous schedule view; the last two lines are e-mail and the REST API:

sudo grep -nE "'(allow.self.registration|disable.reset|view.schedules)' =>" /etc/librebooking/config.php
sudo awk "/^    'email' =>/{f=1} f&&/'enabled'/{print; exit}" /etc/librebooking/config.php
sudo awk "/^    'api' =>/{f=1} f&&/'enabled'/{print; exit}" /etc/librebooking/config.php
141:      'allow.self.registration' => false,
179:      'disable.reset' => true,
187:      'view.schedules' => false,
      'enabled' => false,
      'enabled' => false,

Step 9: Turn on e-mail

E-mail is off because no SMTP relay is configured. While it is off LibreBooking sends no reservation notices or reminders, and password reset by e-mail stays disabled. To turn it on, edit the configuration as root:

sudo nano /etc/librebooking/config.php

Set 'enabled' => true in the 'email' section with your sender address, set 'admin.email' near the top of the file to the address that should receive administrator mail, and fill in the 'phpmailer' section for your relay. For example, for Amazon SES (outbound port 25 is throttled on EC2, so use 587 with STARTTLS):

    'email' =>
    array (
      'enabled' => true,
      'enforce.custom.template' => false,
      'default.from.address' => 'bookings@<your-domain>',
      'default.from.name' => 'Room bookings',
    ),
    ...
    'phpmailer' =>
    array (
      'mailer' => 'smtp',
      'smtp.host' => 'email-smtp.<region>.amazonaws.com',
      'smtp.port' => 587,
      'smtp.secure' => 'tls',
      'smtp.autotls' => true,
      'smtp.auth' => true,
      'smtp.username' => '<smtp-username>',
      'smtp.password' => '<smtp-password>',
      ...
    ),

Then check the file and reload PHP-FPM:

sudo php -l /etc/librebooking/config.php && sudo systemctl reload php8.3-fpm

If you also want people to reset forgotten passwords by e-mail, set 'disable.reset' => false in the 'password' section. Self registration stays off on this image whatever allow.self.registration says, because nginx refuses register.php; add people under Application Management > Users.

LibreBooking's in browser Application Configuration page is off on purpose: the configuration file is read only to PHP, so every setting is changed in config.php as above.

Step 10: Reach an SMTP relay, LDAP directory or SSO provider inside your VPC

LibreBooking runs as the OS user librebooking. A firewall service, librebooking-egress-guard, lets that user reach public internet addresses (such as the public SES endpoint above) but refuses the instance metadata service, link local and loopback addresses, IPv6 and every address in your VPC's own CIDR blocks. That stops anyone who can type a URL into LibreBooking (an OAuth endpoint, a webhook, a mail host) from using it to probe your private network.

If your SMTP relay, LDAP or Active Directory server, OAuth provider or a VPC interface endpoint is on a private address in this VPC, add it to the allow list, one IPv4 address or CIDR per line, and restart the guard:

sudo nano /etc/cloudimg/librebooking-egress-allow.conf
sudo systemctl restart librebooking-egress-guard

Entries may not cover the metadata service, link local, loopback or multicast ranges; the guard refuses to start with such a line.

The image ships with LibreBooking's LDAP, Active Directory, SAML, Shibboleth and other sign-in plugins, and its Google, Microsoft, Facebook, Keycloak and generic OAuth2 sign-in options (the 'authentication' section of config.php), all off. The CAS plugin is not on the image.

Step 11: Your own DNS name and certificate

Point a DNS record at the instance, then set the name LibreBooking is served as. This mints a new self signed certificate for the name, updates nginx, the links LibreBooking puts in its pages and e-mails, the URL and fingerprint in the credentials file, and keeps the name across reboots:

sudo librebooking-set-hostname <your-domain>

To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx (running librebooking-set-hostname again replaces it with a self signed one):

sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/librebooking.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/librebooking.key
sudo nginx -t && sudo systemctl reload nginx

Step 12: Change the administrator password

You can change the password in the web interface under My Account > Change Password. From the command line, librebooking-set-password generates a new random password, or takes yours on standard input (20 to 72 letters, digits and dashes, with an upper case letter, a lower case letter, a digit and a dash). It signs every session out and records the new password in /root/librebooking-credentials.txt. printf is a shell builtin, so the password never appears in the process list; start the line with a space so most shells keep it out of your history:

# a new random password
sudo librebooking-set-password
# or your own
 printf '%s\n' '<new-password>' | sudo librebooking-set-password --stdin

LibreBooking 7.2.0 stores account passwords as salted SHA-1 hashes, which is upstream's design. Give every account a long, unique password, and connect LibreBooking to your directory (Step 10) if you want your own password policy.

Step 13: Data, backups and server components

The versions on the image, and the database (one schema, one administrator account on a new instance):

grep -o "VERSION = '[0-9.]*'" /var/www/librebooking/lib/Config/Configuration.php
php -v | head -1
mysqld --version
nginx -v 2>&1
sudo mysql -N -e "SELECT COUNT(*) FROM librebooking.users"
sudo mysql -N -e "SHOW VARIABLES WHERE Variable_name IN ('skip_networking','log_bin')"
VERSION = '7.2.0'
PHP 8.3.6 (cli) (built: ...) (NTS)
/usr/sbin/mysqld  Ver 8.0.46-0ubuntu0.24.04.4 for Linux on x86_64 ((Ubuntu))
nginx version: nginx/1.24.0 (Ubuntu)
1
log_bin OFF
skip_networking ON

To back up LibreBooking, dump the database and copy the uploaded images (for example to S3 or an EBS snapshot), or take a snapshot of the root volume:

sudo mysqldump --single-transaction librebooking | gzip > librebooking-$(date +%F).sql.gz
sudo tar -czf librebooking-uploads-$(date +%F).tar.gz -C /var/lib/librebooking-public images

To restore a dump into a new instance from this image, stop PHP-FPM, load it, and start PHP-FPM again:

sudo systemctl stop php8.3-fpm
gunzip -c <backup-dir>/librebooking-<date>.sql.gz | sudo mysql librebooking
sudo systemctl start php8.3-fpm

The restored accounts keep their passwords, so the administrator from the old instance signs in, not the one in the new credentials file.

Component Path
LibreBooking code (root owned, read only to PHP) /var/www/librebooking
Configuration /etc/librebooking/config.php (root:librebooking 0640)
Licence, source note and upstream composer.lock /usr/share/doc/librebooking/
Sessions, compiled templates, cache /var/lib/librebooking
Uploaded images (resource pictures, logo) /var/lib/librebooking-public/images
Application log /var/log/librebooking/app.log
Database MySQL 8.0, database and user librebooking, unix socket only
nginx site /etc/nginx/sites-available/cloudimg-librebooking
Certificate and key /etc/nginx/ssl/
VPC allow list for LibreBooking /etc/cloudimg/librebooking-egress-allow.conf
Helpers /usr/local/sbin/librebooking-set-password, librebooking-set-hostname, librebooking-cred-roundtrip
First boot script /usr/local/sbin/librebooking-firstboot.sh
Credentials /root/librebooking-credentials.txt (mode 0600)

Logs you may notice. LibreBooking writes each refused sign-in to /var/log/librebooking/app.log as Failed user authentication for <name>, with the name typed but never the password. While the administrator is the only account, every sign-in also adds a PHP warning to /var/log/nginx/error.log that file_put_contents(.../config/config.php) was refused: upstream tries to rewrite its configuration file after the first sign-in, and on this image that file is read only to PHP, so nothing changes. The warning stops once you add a second account.

Step 14: Security recommendations

  • Allow port 22 in the security group from your own networks only, and ports 443 and 80 only to the people who book resources.
  • Check the certificate fingerprint the first time you connect (Step 5), then use your own DNS name and a certificate your clients trust (Step 11).
  • Change the administrator's e-mail address (Step 5) and keep self registration off; add people yourself.
  • Allow only the private hosts LibreBooking really needs (Step 10).
  • Keep the operating system patched; unattended security upgrades are on, and sudo apt-get update && sudo apt-get upgrade applies everything else.

Support and licensing

LibreBooking is open source under the GNU General Public License v3.0; there is no per user or per CPU licence fee. The licence, a note on where the source comes from and upstream's original composer.lock are in /usr/share/doc/librebooking/. cloudimg is not affiliated with or endorsed by the LibreBooking project. The AMI price covers cloudimg's hardening, first boot setup and support.

  • Email: support@cloudimg.co.uk
  • Website: https://www.cloudimg.co.uk
  • Support: 24/7, with a 24 hour response SLA