S4
Applications AWS

SPIP 4.4.28 on AWS User Guide

| Product: SPIP 4.4.28 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the SPIP 4.4.28 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.

SPIP (Système de Publication pour l'Internet) is a mature open source content management system built for editorial and publishing websites: articles organised in sections, an editorial workflow (articles are proposed, reviewed and published), authors with roles, multilingual content, documents and images, and a public site rendered from templates (skeletons). It is widely used for newsrooms, associations, public bodies and documentation sites. This is SPIP the CMS, not spiped the secure pipe daemon.

The cloudimg image adds:

  • No reachable web installer, no shared database. A plain SPIP installation offers its web installer (ecrire/?exec=install) to whoever opens it first, and that person chooses the database and creates the first webmestre. This image ships no database at all: the MariaDB data directory is empty in the image, and your instance's first boot initialises its own database, runs SPIP's own installer over a private loopback connection before the web server starts, and creates the webmestre admin with a password generated on your instance. nginx refuses the installer address afterwards, and SPIP itself refuses it once it is installed.
  • Secrets of your instance's own. The database password, the webmestre password and SPIP's secret keys (config/cles.php, which sign SPIP's forms and actions and pepper every password hash) are generated on your instance's first boot. Nothing from the image build is reused.
  • The current release. SPIP 4.4.28 is the newest stable release. It fixes the security issues reported against earlier 4.4 releases, among them two remote code execution flaws exploited in the wild in August 2026 (fixed in 4.4.20 and 4.4.21), a blind SQL injection, missing authorisation checks on private actions and a server-side request forgery through redirects. The download is verified against a pinned checksum, and SPIP's security screen (config/ecran_securite.php, version 1.6.7) is in place and active.
  • HTTPS from the first boot. nginx serves SPIP on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
  • Fail closed. nginx does not start until first boot has finished; if first boot cannot complete, the site stays offline rather than open.
  • Only SPIP's entry points run. PHP runs only through spip.php, index.php and the private area ecrire/. SPIP's configuration (config/), its working folder (tmp/: sessions, caches, logs) and its libraries are never served, no other PHP file in the installation can be called, and documents in IMG/ are served as static files only, with a sandboxing content security policy for SVG and HTML files.
  • A rate-limited login. SPIP does not throttle sign in attempts. On this image nginx limits sign in and password reset requests to 6 a minute from one address after a burst of 10, the address comes from the connection (never from a header a client sends), and SPIP accepts the sign in form only on its own page so the limit cannot be sidestepped. HTTP Basic authentication is switched off.
  • Hardened defaults. The SPIP code and configuration belong to root and are read only to PHP; PHP cannot run shell commands and is confined to SPIP's folders. MariaDB has no network listener at all (SPIP talks to it over a local socket) and its root account signs in only through the socket as the operating system's root. nginx, PHP and MariaDB are blocked from the instance metadata service, the AMI requires IMDSv2, requests for a host name the instance does not serve are dropped, and SPIP always sees the host name you configure, never one a request claims.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: SPIP 4.4.28 (the official release with its bundled plugins, checksum verified), MariaDB 10.11, PHP 8.3 FPM, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. The database and SPIP's documents are on a separate 30 GiB EBS volume mounted at /var/lib/spip.

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 (SPIP) and TCP 80 (redirects to HTTPS) from the networks that should reach them. Keep 22 to your own networks. Open 443 to your readers for a public site, or only to your editors while you prepare it.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick SPIP 4.4.28 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 (the database and documents) in the launch settings; you can make it larger there.

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=spip}]'

Step 2: Connect to your instance

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

Step 3: Check the services

First boot takes about half a minute (it creates the database and runs SPIP's installer). Then nginx, PHP, MariaDB and the first boot unit are active:

systemctl is-active nginx php8.3-fpm mariadb spip-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 SPIP release, the security screen, the PHP and MariaDB versions and the data volume:

grep "^\$spip_version_branche = " /var/www/spip/ecrire/inc_version.php
sudo grep "define('_ECRAN_SECURITE'" /var/www/spip/config/ecran_securite.php
php -v | head -1
mariadb --version
findmnt -no TARGET,FSTYPE,SIZE /var/lib/spip
$spip_version_branche = '4.4.28';
define('_ECRAN_SECURITE', '1.6.7'); // 2025-09-08
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
/var/lib/spip ext4   29.4G

Step 4: Get your sign-in details

The credentials file holds the address SPIP is served as (your instance's public DNS name by default), the private area address, the webmestre login and password, and the database credentials, all generated on this instance. Show it with:

sudo cat /root/spip-credentials.txt
SPIP_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
SPIP_ADMIN_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ecrire/
SPIP_ADMIN_USER=admin
SPIP_ADMIN_PASSWORD=<24 letters and digits>
SPIP_DB_NAME=spip
SPIP_DB_USER=spip
SPIP_DB_PASSWORD=<48 hexadecimal characters>

Step 5: Visit the site and sign in

Open SPIP_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). The public site shows a welcome article that first boot created so a new site renders real content:

The public SPIP site rendered by the default templates, with the Welcome section and the Welcome to your SPIP site article

Open SPIP_ADMIN_URL (the private area, /ecrire/). SPIP sends you to its sign in page; sign in with the login admin and SPIP_ADMIN_PASSWORD:

The SPIP sign in page with the Login and Password fields

The private area opens on your home page, with the latest editorial activity and the menus Edit, Publish, Templates, Maintenance and Configuration:

The SPIP private area home page after signing in as the webmestre

Open your own author page from the top bar to set your name and e-mail address (it starts as admin@localhost.localdomain) and to change the password.

Step 6: Write and publish

SPIP organises a site in sections (rubriques), which hold articles and may hold sub-sections. Under Edit > Sections create the sections of your site; inside a section choose Write a new article, give it a title and a text (SPIP's typographic shortcuts are shown under the text field), and save. A new article is in progress; set it to proposed for evaluation for review, and to published online to put it on the public site.

The SPIP articles list in the private area with the welcome article and its status

Authors are added under Edit > Authors. A webmestre administers everything, an administrator manages content (optionally restricted to some sections), and an editor (rédacteur) writes and proposes articles. Give each person their own account with the narrowest status that works.

Step 7: Site configuration and plugins

Configuration > Site identity sets the site name, slogan and description; Configuration > Interactivity, Site content and Multilingual switch on forums, the fields articles use and translations. Configuration > Manage plugins lists the plugins: the 20 plugins bundled with SPIP (archives, uploads, documents, keywords, forums, statistics, referenced sites and more) are always active and listed under Locked.

The SPIP Manage plugins page, Locked tab, listing the 20 plugins bundled with SPIP

Plugins you add (Add plugins, from SPIP's plugin repositories) go into /var/www/spip/plugins/auto, the one code folder the web server may write. Only install plugins you trust: a plugin is PHP code that runs with SPIP's rights.

Email: SPIP sends password reset links and notifications with PHP's mail function, and the image has no mail server. Add the Facteur plugin (Manage plugins > Add plugins) to send through your SMTP server before you rely on e-mail. The webmestre password can always be reset from the shell (Step 12).

Step 8: Your data

SPIP's data is on its own EBS volume at /var/lib/spip: the MariaDB data directory (mysql, initialised on this instance's first boot) and SPIP's documents (IMG, which the site serves as /IMG/):

sudo ls -l /var/lib/spip | sed -E 's/ +/ /g' | cut -d' ' -f1,3,4,9-
total
drwxr-x--- www-data www-data IMG
drwx------ root root lost+found
drwxr-x--- mysql mysql mysql

The site's configuration is in /var/www/spip/config: connect.php (the database connection) and cles.php (SPIP's secret keys) were written by SPIP's installer on first boot. Keep cles.php with your backups: every password hash and every signed form depends on it, so a database restored without its cles.php has no working passwords.

MariaDB has four accounts: the system accounts root and mysql (which sign in only through the local socket as the matching operating system user), the locked mariadb.sys, and spip, SPIP's own user with this instance's password. It has no network listener:

sudo mysql -N -e "SELECT CONCAT(User,'@',Host) FROM mysql.global_priv ORDER BY 1; SELECT @@skip_networking, @@datadir"
mariadb.sys@localhost
mysql@localhost
root@localhost
spip@localhost
1   /var/lib/spip/mysql/

Back the site up with EBS snapshots of the data volume and the root volume, or as files:

sudo mysqldump --single-transaction spip | gzip > spip-db.sql.gz
sudo tar -czf spip-files.tar.gz -C / var/lib/spip/IMG var/www/spip/config/cles.php var/www/spip/config/connect.php

SPIP's own Maintenance > Backup the database in the private area writes a backup into tmp/dump/ as well.

Step 9: Security checks

Port 80 sends browsers to HTTPS, the health endpoint answers for load balancer checks, and the installer, the configuration, SPIP's working folder and its internal PHP files are not reachable:

H=$(sed -n 's/^set \$spip_host "\(.*\)";$/\1/p' /etc/nginx/spip-host.conf)
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/ecrire/"
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/healthz"
for p in '/ecrire/?exec=install' /config/connect.php /config/cles.php /tmp/meta_cache.php /ecrire/inc_version.php /vendor/autoload.php; do
  curl -sk -o /dev/null -w "$p: %{http_code}\n" --resolve "$H:443:127.0.0.1" "https://$H$p"
done
curl -s -o /dev/null -w 'unknown host name: %{http_code}\n' -H 'Host: example.com' http://127.0.0.1/ || true
port 80: 301 -> https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ecrire/
ok
/ecrire/?exec=install: 404
/config/connect.php: 404
/config/cles.php: 404
/tmp/meta_cache.php: 404
/ecrire/inc_version.php: 404
/vendor/autoload.php: 404
unknown host name: 000

(000 means nginx closed the connection without an answer.) The private area is closed until you sign in:

H=$(sed -n 's/^set \$spip_host "\(.*\)";$/\1/p' /etc/nginx/spip-host.conf)
curl -sk -o /dev/null -w 'private area: %{http_code} -> %{redirect_url}\n' --resolve "$H:443:127.0.0.1" "https://$H/ecrire/"
private area: 302 -> https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/spip.php?page=login&url=%2Fecrire%2F

The credentials file is readable by root only, the configuration by root and the web server's group, SPIP's working folder and documents by the web server only, and the code belongs to root:

sudo stat -c '%a %U:%G %n' /root/spip-credentials.txt /var/www/spip/config/connect.php /var/www/spip/config/cles.php /var/www/spip/config /var/www/spip/tmp /var/lib/spip/IMG /var/www/spip/spip.php
600 root:root /root/spip-credentials.txt
640 root:www-data /var/www/spip/config/connect.php
640 root:www-data /var/www/spip/config/cles.php
750 root:www-data /var/www/spip/config
750 www-data:www-data /var/www/spip/tmp
750 www-data:www-data /var/lib/spip/IMG
644 root:root /var/www/spip/spip.php

The certificate was generated on this instance and names its own addresses:

H=$(sed -n 's/^set \$spip_host "\(.*\)";$/\1/p' /etc/nginx/spip-host.conf)
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
subject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = cloudimg
X509v3 Subject Alternative Name:
    DNS:ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost

nginx, PHP and MariaDB cannot reach the instance metadata service; PHP cannot run shell commands and is confined to SPIP's folders; nginx hands PHP the configured host name and the real client address from the connection (never values a client sends, and never an Authorization header), and rate-limits sign in requests:

for u in nginx php8.3-fpm mariadb; do systemctl show $u -p IPAddressDeny; done
php-fpm8.3 -i 2>/dev/null | grep -E '^(open_basedir|disable_functions|expose_php) ' | cut -d' ' -f1-3
grep -E 'fastcgi_param (HTTP_HOST|SERVER_NAME|HTTP_X_FORWARDED_FOR|HTTP_X_FORWARDED_HOST|HTTP_AUTHORIZATION) ' /etc/nginx/snippets/cloudimg-spip-php.conf
grep -h 'limit_req_zone\|limit_req_status' /etc/nginx/conf.d/cloudimg-spip-login-guard.conf
IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32
IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32
IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32
disable_functions => exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec
expose_php => Off
open_basedir => /var/www/spip/:/var/lib/spip/IMG/:/var/lib/php/sessions/:/tmp/:/usr/share/php/
fastcgi_param HTTP_HOST $spip_host;
fastcgi_param SERVER_NAME $spip_host;
fastcgi_param HTTP_X_FORWARDED_HOST "";
fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr;
fastcgi_param HTTP_AUTHORIZATION "";
limit_req_zone $spip_auth_key zone=spip_auth:10m rate=6r/m;
limit_req_status 429;

If a burst of wrong passwords rate-limits you (HTTP 429), wait a minute and try again: the limit only slows down repeated attempts from one address.

sudo spip-cred-roundtrip checks the whole sign in path on the instance: the redirect to HTTPS, the refused installer and configuration, a sign in with the password from the credentials file, and that wrong and common passwords are refused.

Step 10: Server-side image processing

SPIP makes thumbnails and resized images with PHP's GD library, which works out of the box. PHP on this image cannot run shell commands, so the optional ImageMagick (convert) and netpbm methods under Configuration > Advanced functions are not available (their test images on that page stay broken); keep GD selected.

Step 11: Your own DNS name and certificate

Point a DNS record at the instance, then set the name SPIP is served as. This mints a new self signed certificate for the name, updates nginx, SPIP's site address (used in feeds, e-mails and absolute links) and the addresses in /root/spip-credentials.txt, clears SPIP's caches and keeps the name across reboots:

sudo spip-set-hostname <your-domain>

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

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

Once browsers trust the certificate you can also add an HSTS header (add_header Strict-Transport-Security "max-age=31536000" always;) to /etc/nginx/snippets/cloudimg-spip-headers.conf. The image does not send one: browsers ignore HSTS from a site whose certificate they do not trust.

Step 12: Passwords and upgrades

spip-set-password gives the webmestre admin a new random password with SPIP's own password scheme and records it in /root/spip-credentials.txt. Run it without arguments, or pass your own password (24 to 96 letters and digits) on stdin. Authors change their own password on their author page, and the webmestre can change any author's.

sudo spip-set-password
printf '%s\n' '<your-new-password>' | sudo spip-set-password --stdin

Security screen. SPIP's security screen blocks known attack patterns before SPIP runs. Update it whenever SPIP announces a new version (snapshot first):

curl -fsSL -o /tmp/ecran_securite.php https://git.spip.net/spip-contrib-outils/securite/raw/branch/master/ecran_securite.php
php -l /tmp/ecran_securite.php && sudo install -o root -g root -m 0644 /tmp/ecran_securite.php /var/www/spip/config/ecran_securite.php

Upgrades. Snapshot the instance, download the new release zip from https://files.spip.net/spip/archives/, read its release notes, and replace the code as root while keeping config, IMG, tmp, local, plugins/auto and lib:

sudo apt-get install unzip
unzip -q spip-v<version>.zip -d /tmp/spip-new
sudo systemctl stop nginx
cd /var/www/spip && sudo rm -rf ecrire prive plugins-dist squelettes-dist vendor spip.php index.php
sudo cp -a /tmp/spip-new/{ecrire,prive,plugins-dist,squelettes-dist,vendor,spip.php,index.php} /var/www/spip/
sudo chown -R root:root /var/www/spip/{ecrire,prive,plugins-dist,squelettes-dist,vendor,spip.php,index.php}
sudo systemctl start nginx

Then sign in to the private area: SPIP runs its database upgrade itself when the webmestre visits it.

Step 13: Server components

Component Path
SPIP code (root owned) /var/www/spip
Configuration: database connection, secret keys, site options /var/www/spip/config/ (connect.php, cles.php, mes_options.php)
Security screen /var/www/spip/config/ecran_securite.php
Working folder: sessions, caches, logs, backups /var/www/spip/tmp, /var/www/spip/local
Database (MariaDB datadir) and documents /var/lib/spip/mysql, /var/lib/spip/IMG (their own EBS volume)
nginx site and login rate limit /etc/nginx/sites-available/cloudimg-spip, /etc/nginx/conf.d/cloudimg-spip-login-guard.conf
PHP settings /etc/php/8.3/fpm/conf.d/99-cloudimg-spip.ini
MariaDB settings /etc/mysql/mariadb.conf.d/99-cloudimg-spip.cnf
Certificate and key /etc/nginx/ssl/
First boot script /usr/local/sbin/spip-firstboot.sh
Helpers /usr/local/sbin/spip-set-hostname, spip-set-password, spip-cred-roundtrip
Licence, changelog and image notes /usr/share/doc/spip/
Credentials /root/spip-credentials.txt (mode 0600)

Step 14: Security recommendations

  • Allow port 22 in the security group from your own networks only.
  • Keep the webmestre password secret and rotate it if it may have been shared (Step 12). Give each person their own author account with the narrowest status that works.
  • Keep config/cles.php secret and backed up with the database (Step 8).
  • Use your own DNS name and a certificate your browsers trust (Step 11).
  • Keep SPIP and its security screen current (Step 12) and install only plugins you trust.
  • Keep the operating system patched; unattended security upgrades are on, and sudo apt-get update && sudo apt-get upgrade applies everything else. MariaDB, PHP-FPM and PHP's zip extension come from Ubuntu's universe repository; sudo pro security-status --esm-apps shows whether Ubuntu Pro (ESM Apps) has security updates for them (none were pending when the image was built).
  • Snapshot the data volume regularly (Step 8).

Support and licensing

SPIP is free software under the GNU General Public License version 3 (a copy is at /usr/share/doc/spip/LICENSE); there is no per user or per CPU licence fee. This image installs the unmodified upstream release, without its test suites, plus the cloudimg site options file config/mes_options.php described above. 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