Ibexa OSS 5.0.10 on AWS User Guide
Overview
This guide covers launching and using the Ibexa OSS 5.0.10 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg BookStack, CMS, Blogs and Wikis listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
Ibexa OSS is the open source edition of the Ibexa content platform (the successor of eZ Platform and eZ Publish), built on Symfony 7.4 and PHP 8.3. You model your content with content types and fields, organise it in a content tree with sections, languages, roles and policies, edit it in the back office at /admin with a rich text editor, and serve it through Symfony templates. A REST API and a GraphQL API come with it.
The cloudimg image adds:
- No shared database and no default password. A stock Ibexa installation creates two accounts with the well-known password
publish: the administratoradminand theanonymousaccount every visitor uses. This image ships no database at all (the MariaDB data directory is empty) and no.env.local(noAPP_SECRET, no database password). Your instance's first boot initialises its own database, writes its own.env.local, runs Ibexa's installer and replaces the password of both accounts with values generated on your instance before the web server or PHP start, sopublishnever signs anyone in. - Secrets of your instance's own, never on a command line. The database password,
APP_SECRET, the administrator password and the TLS key are generated on your instance; nothing from the image build is reused. Passwords are handed to Ibexa in files only root and the web server can read, never as command line arguments. - The current release with refreshed Symfony. Ibexa OSS 5.0.10 is the newest release (it fixes Ibexa's security advisory IBEXA-SA-2026-004). Its lock file pins
symfony/http-foundation7.4.16, which has a denial of service any visitor can trigger with a request header (CVE-2026-108082); this image installs Ibexa from that lock and then updates the Symfony packages to their latest 7.4 patch releases inside Ibexa's own version constraints (Step 3 lists every package that changed). - Closed by default. The REST API answers anonymous callers
401(signing in is the only public call), the GraphQL endpoint, the GraphiQL console and the API documentation are not served, the Symfony profiler does not exist (production mode) and public registration is off. - HTTPS from the first boot. nginx serves Ibexa on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
- Fail closed. nginx and PHP do not start until first boot has finished; if first boot cannot complete, the site stays offline rather than open.
- Only the front controller runs PHP. Every request goes through Symfony's
public/index.php; every other PHP file is never run. The content storage that holds uploaded images and files (/var/...) and Ibexa's file downloads (/content/download/...) are served withnosniffand a sandboxing content security policy on every response, and only passive types (images, PDF, fonts, audio and video) open in the browser; anything else is sent as a download. - A rate-limited sign in. nginx gives every sign-in route (the site and back-office sign in, the REST API session, forgotten and reset password, registration and password change) one shared budget of 20 a minute from one address after a burst of 20. nginx uses the address of the connection, never a header a client sends.
- The real host and client address. nginx hands Ibexa the host name you configure and nothing a request claims, and Symfony's trusted host list and default URI are pinned to it, so every link, password reset e-mails included, uses your host name. nginx removes
X-Forwarded-For,Client-IPand the other forwarding headers before PHP sees them. - Links from other sites cannot sign you out or change state. Signing out, marking notifications read, the content create and translate shortcuts, drafts, focus mode and emptying the trash only follow links opened from your own site.
- No calls home and no third-party scripts. Measured on the build: the server makes no outbound connection while the back office and the site are used, and the site's welcome page no longer loads Google Fonts.
- Hardened defaults. The Ibexa code belongs to root and is read only to PHP; only
var/and the content storage are writable (and kept private to the web server). PHP cannot run shell commands. MariaDB has no network listener at all (Ibexa talks to it over a local socket) and itsrootaccount signs in only through the socket as the operating system's root. nginx, PHP, MariaDB and the first boot service are blocked from the instance metadata service, the AMI requires IMDSv2, and requests for a host name the instance does not serve are dropped. - Your data on its own volume. The database and the content storage live on a separate 30 GiB EBS volume mounted at
/var/lib/ibexa, which you can grow or snapshot on its own. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Ibexa OSS 5.0.10 (installed with composer from the release lock, Symfony 7.4 refreshed), MariaDB 10.11, PHP 8.3 FPM, nginx, composer, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. Node.js is not on the image: the back-office assets were compiled when the image was built.
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 (Ibexa) and TCP 80 (redirects to HTTPS) from the networks that should reach them. Keep 22 to your own networks. Open 443 to your visitors for a public site, or only to your team while you build it.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Ibexa OSS 5.0.10 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. Keep the second volume (30 GiB, the data volume): first boot will not set up Ibexa without it.
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=ibexa}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| Ibexa OSS 5.0.10 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot takes about a minute (it creates the database and installs Ibexa). Then nginx, PHP, MariaDB and the first boot unit are active:
systemctl is-active nginx php8.3-fpm mariadb ibexa-firstboot
active
active
active
active
nginx serves ports 80 and 443; PHP and MariaDB listen on local sockets only, so the only open ports are 22, 80 and 443:
sudo ss -tlnp | awk 'NR==1 || /nginx|sshd|php|maria/' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'
State Local Peer
LISTEN 0.0.0.0:22 users:(("sshd"),("systemd"))
LISTEN 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
LISTEN 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
LISTEN [::]:22 users:(("sshd"),("systemd"))
LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx"))
LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx"))
The Ibexa release, the PHP and MariaDB versions, and the Symfony security fix:
python3 -c 'import json; P={p["name"]: p["version"] for p in json.load(open("/var/www/ibexa/vendor/composer/installed.json"))["packages"]}; [print(n, P[n]) for n in ("ibexa/oss", "ibexa/core", "symfony/http-foundation", "symfony/yaml", "symfony/expression-language")]'
php -v | head -1
mariadb --version
ibexa/oss v5.0.10
ibexa/core v5.0.10
symfony/http-foundation v7.4.21
symfony/yaml v7.4.21
symfony/expression-language v7.4.21
PHP 8.3.6 (cli) (built: Oct 19 2026 15:16:58) (NTS)
mariadb Ver 15.1 Distrib 10.11.14-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper
How the release was installed, and every package the Symfony refresh changed:
cat /usr/share/doc/ibexa/RELEASE
head -8 /usr/share/doc/ibexa/SYMFONY-UPDATE.txt
Ibexa OSS 5.0.10 (ibexa/oss-skeleton v5.0.10, commit 49630948f8736c5d97b35d9aa51f2bea7ded091b)
licence GPL-2.0-only (the open-source edition; cloudimg does not grant or imply the Ibexa BUL)
skeleton composer.lock sha256 270ad5a59779d348680dd4459510d540b12cebf5685767291c787944ab9bab5f
composer 2.10.3 (sha256 7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6); build-only Node.js v22.23.4 (sha256 1805bcd35e6815559bd2eb97db438d3fdb0eb3a5e6a977b7c1badca76337e9ce)
Ibexa OSS 5.0.10: d-575(6) composer update 'symfony/*' --with-dependencies at build (2026-10-28T01:56:38Z UTC)
package before after
doctrine/lexer 3.0.1 3.0.3
monolog/monolog 3.10.0 3.12.1
phpdocumentor/reflection-common 2.2.0 2.2.1
phpstan/phpdoc-parser 2.3.3 2.3.9
symfony/cache v7.4.16 v7.4.21
symfony/clock v7.4.8 v7.4.21
The full table is in /usr/share/doc/ibexa/SYMFONY-UPDATE.txt and the security audit of the installed packages in /usr/share/doc/ibexa/composer-audit.json. The one advisory it lists is a low severity issue in league/flysystem 2.5 (a path check for control characters), which Ibexa only reaches with file names an editor uploads.
Step 4: Get your sign-in details
The credentials file holds the address Ibexa is served as (your instance's public DNS name by default), the back-office address, the administrator login and password, and the database credentials, all generated on this instance. Show it with:
sudo cat /root/ibexa-credentials.txt
IBEXA_SITE_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
IBEXA_ADMIN_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/admin/login
IBEXA_ADMIN_LOGIN=admin
IBEXA_ADMIN_PASSWORD=<24 letters and digits>
IBEXA_DB_NAME=ibexa
IBEXA_DB_USER=ibexa
IBEXA_DB_PASSWORD=<48 hexadecimal characters>
Step 5: Visit the site and sign in
Open IBEXA_SITE_URL in your browser. The certificate is self signed (generated on your instance), so accept the browser warning once, or install your own certificate (Step 11). A fresh site shows Ibexa's welcome page:

Open IBEXA_ADMIN_URL and sign in with the login admin and IBEXA_ADMIN_PASSWORD:

The back office opens on its Dashboard:

admin is an Administrator. Create a separate account for each editor (Users and Roles are on the administration menu behind the gear icon at the bottom of the menu, with Languages, URL management and System information) and give it a role with only the policies it needs.
Step 6: Content types, content and the site
Ibexa organises everything as content in a tree: Content structure holds the site, Media holds images and files, and the users are content too. To add a page, open Content structure, choose Create content, pick a content type (for example Article or Folder), fill in the fields and Publish:

Content types (Content types in the menu, under Settings) define the fields each kind of content has. Uploaded images and files are stored under /var/lib/ibexa/var on the data volume. The site's templates live in /var/www/ibexa/templates, and the Ibexa documentation explains how to build your own design with Symfony and Twig.
Step 7: The closed-by-default settings
The REST API is closed to anonymous callers: only signing in (POST /api/ibexa/v2/user/sessions) is public, and everything else answers 401 until you are signed in (the back office uses it with your session). GraphQL, the GraphiQL console and the API documentation are not served, and the Symfony profiler does not exist:
H=$(sudo sed -n 's#^IBEXA_SITE_URL=https://\(.*\)/$#\1#p' /root/ibexa-credentials.txt)
for u in /api/ibexa/v2/content/locations/1/2 /graphql /api/docs /graphiql /_profiler /register; do
printf '%-36s %s\n' "$u" "$(curl -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code}' "https://$H$u")"
done
/api/ibexa/v2/content/locations/1/2 401
/graphql 404
/api/docs 404
/graphiql 404
/_profiler 404
/register 401
The image serves two siteaccesses: site, the default (no prefix; Ibexa also answers it as /site/...), and admin, the back office at /admin. Ibexa picks the siteaccess from the first part of the path, so every closed route and every rule above also applies under /site/, /admin/ or any other first segment.
What an anonymous visitor can do: view the published site (content in the Standard section; the welcome page on a fresh site), search it, open the sign in and forgotten password forms, and load the site's static files and the public JavaScript route and translation lists the back office uses. Registration is off because the Anonymous role has no user/register policy.
To open the REST API to anonymous readers again, delete /var/www/ibexa/src/EventSubscriber/CloudimgRestAnonymousSubscriber.php and clear the cache (sudo -u www-data php /var/www/ibexa/bin/console cache:clear). To serve GraphQL, remove the graphql line from the $ibx_closed map in /etc/nginx/conf.d/cloudimg-ibexa-guard.conf and reload nginx; the schema is already generated for your content types (/var/www/ibexa/config/graphql/types/ibexa), and the endpoint uses the same sign in and permissions as the back office.
Step 8: Your data
The database and the content storage are on the data volume, a separate EBS volume mounted at /var/lib/ibexa:
findmnt -no SOURCE,FSTYPE,SIZE,TARGET /var/lib/ibexa
sudo ls /var/lib/ibexa
sudo mysql -N -e "SELECT @@datadir, @@skip_networking"
/dev/nvme1n1 ext4 29.4G /var/lib/ibexa
lost+found
mysql
var
/var/lib/ibexa/mysql/ 1
mysql is MariaDB's data directory and var is Ibexa's content storage (served as /var/...). Back up by snapshotting the volume, or with sudo mysqldump ibexa plus a copy of /var/lib/ibexa/var. Keep /var/www/ibexa/.env.local (this instance's APP_SECRET and database password) with your backups and keep it secret.
Step 9: Security checks
The well-known installer password is refused for both built-in accounts, and the REST API refuses an anonymous read; the round trip helper also proves your own password signs in through the back-office form:
sudo ibexa-cred-roundtrip
OK https 301 + admin login + wrong and 'publish' passwords refused (form and REST, admin and anonymous) + anonymous REST 401
PHP runs only through Symfony's front controller, and the content storage is static only with a sandboxing policy:
H=$(sudo sed -n 's#^IBEXA_SITE_URL=https://\(.*\)/$#\1#p' /root/ibexa-credentials.txt)
for u in /index.php /index.php/admin/login /var/x.php /.env.local /composer.json; do
printf '%-26s %s\n' "$u" "$(curl -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code}' "https://$H$u")"
done
grep -A3 'map "$ibx_upload$ibx_passive" $ibx_up_csp' /etc/nginx/conf.d/cloudimg-ibexa-guard.conf | sed -n 3p
/index.php 404
/index.php/admin/login 404
/var/x.php 404
/.env.local 404
/composer.json 404
"~^1" "default-src 'none'; img-src 'self' data:; media-src 'self'; style-src 'unsafe-inline'; sandbox";
The services are blocked from the instance metadata service, and the instance requires IMDSv2:
for u in nginx php8.3-fpm mariadb ibexa-firstboot; do printf '%-16s %s\n' "$u" "$(systemctl show -p IPAddressDeny --value "$u")"; done
curl -s -o /dev/null -w 'IMDSv1 from the shell: %{http_code}\n' http://169.254.169.254/latest/meta-data/
nginx 169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
php8.3-fpm 169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
mariadb 169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
ibexa-firstboot 169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
IMDSv1 from the shell: 401
SSH accepts keys only, the code belongs to root, and .env.local is readable by the web server only:
sudo sshd -T | grep -E '^(passwordauthentication|permitrootlogin|x11forwarding) '
stat -c '%U:%G %a %n' /var/www/ibexa /var/www/ibexa/vendor /var/www/ibexa/var /var/www/ibexa/.env.local /var/lib/ibexa/var
permitrootlogin no
passwordauthentication no
x11forwarding no
root:root 755 /var/www/ibexa
root:root 755 /var/www/ibexa/vendor
www-data:www-data 750 /var/www/ibexa/var
root:www-data 640 /var/www/ibexa/.env.local
www-data:www-data 750 /var/lib/ibexa/var
Step 10: Behind a proxy or load balancer
nginx removes every forwarding header and Symfony trusts no proxy, so behind a load balancer Ibexa records the balancer's address. If you terminate TLS on an Application Load Balancer, keep the target on port 443 (the instance's own certificate) and set the host name with ibexa-set-hostname (Step 11). To trust a balancer's X-Forwarded-For, add its subnet as SYMFONY_TRUSTED_PROXIES in /var/www/ibexa/.env.local and remove the fastcgi_param HTTP_X_FORWARDED_FOR ""; line from /etc/nginx/snippets/cloudimg-ibexa-fastcgi.conf; only do this when the instance is not reachable directly.
Step 11: Your own DNS name and certificate
Point a DNS name at the instance, then pin Ibexa to it. This mints a new self-signed certificate for the name, pins nginx, Symfony's trusted hosts and default URI, and the cross-site guard to it:
sudo ibexa-set-hostname cms.example.com
For a public certificate, install certbot (sudo apt install certbot), obtain a certificate for the name with the webroot or DNS method, and point ssl_certificate and ssl_certificate_key in /etc/nginx/sites-available/cloudimg-ibexa at it, then sudo nginx -t && sudo systemctl reload nginx. Add HSTS once the certificate is trusted.
Step 12: Passwords, e-mail, updates
- Administrator password:
sudo ibexa-set-passwordgenerates a new one, writes it to/root/ibexa-credentials.txtand signs every session out;sudo ibexa-set-password admin --stdinsets one you type (24 to 96 letters and digits with an upper case letter, a lower case letter and a digit). Other accounts:sudo ibexa-set-password <login>. - E-mail: the image sends no mail (
MAILER_DSN=null://null). SetMAILER_DSNin/var/www/ibexa/.env.localto your SMTP service (for example Amazon SES), for password reset and notification e-mails. - Operating system updates: unattended upgrades apply Ubuntu security updates automatically.
- Ibexa and Symfony updates: Ibexa is maintained with composer in
/var/www/ibexa. Installunzipandgitfirst (sudo apt install unzip git), take a snapshot of both volumes, then run composer as root for the code (the code belongs to root). An update that changes the back-office assets also needs Node.js 22 and yarn to rebuild them (composer run-script auto-scripts); follow Ibexa's upgrade documentation, then clear the cache aswww-data. Check advisories withsudo composer audit --working-dir=/var/www/ibexa.
Step 13: Server components
| Component | Where |
|---|---|
| Ibexa OSS 5.0.10 | /var/www/ibexa (code, root-owned), /var/www/ibexa/.env.local (this instance's secrets) |
| Content storage | /var/lib/ibexa/var on the data volume, served as /var/... |
| MariaDB 10.11 | data directory /var/lib/ibexa/mysql on the data volume, local socket only |
| PHP 8.3 FPM | /etc/php/8.3/fpm/conf.d/99-cloudimg-ibexa.ini |
| nginx | /etc/nginx/sites-available/cloudimg-ibexa, /etc/nginx/conf.d/cloudimg-ibexa-guard.conf |
| First boot | ibexa-firstboot.service, /usr/local/sbin/ibexa-firstboot.sh |
| Helpers | ibexa-set-hostname, ibexa-set-password, ibexa-cred-roundtrip in /usr/local/sbin |
| Notes | /usr/share/doc/ibexa/CLOUDIMG.txt |
Step 14: Security recommendations
- Keep SSH (22) open only to your own networks, and 443 only to the visitors who need it while you build the site.
- Give every editor their own account with a role that has only the policies they need; keep
adminfor administration. - Set up a real DNS name and certificate (Step 11) and e-mail (Step 12) before you go live.
- Snapshot the data volume regularly, and keep
.env.localwith your backups. - Watch Ibexa's security advisories and apply composer updates as Step 12 describes.
Support and licensing
Ibexa OSS is open source software distributed under the GNU General Public License version 2 (GPL-2.0-only); this image ships it under that licence. The licence text and Ibexa's copyright notice are in /usr/share/doc/ibexa. Ibexa's commercial editions (Ibexa DXP) and their Business Use License are separate products that this image does not include. Ibexa and eZ Platform are trademarks of Ibexa AS; cloudimg is not affiliated with or endorsed by Ibexa AS. cloudimg provides support for the image: see cloudimg support.