Applications AWS

Directory Lister 5.8.1 on AWS User Guide

| Product: Directory Lister 5.8.1 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Directory Lister 5.8.1 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.

Directory Lister is an open source web application that turns a folder of files into a clean, styled listing in the browser. Visitors move through folders with breadcrumbs, search by file name, read README files rendered on the page, check a file's MD5, SHA-1 and SHA-256 hashes, download single files and download a whole folder as a zip archive. It needs no database: it reads the files straight from the disk.

The cloudimg image adds:

  • Private by default. Out of the box Directory Lister has no sign-in, so every file you put in its folder is public at once. On this image every visitor signs in as admin with a password generated on your instance's first boot. The password is stored on the instance only as a bcrypt hash for the web server and in /root/directory-lister-credentials.txt (readable by root only). Nothing from the image build ships in the image. Making the listing public is a deliberate step you take yourself (Step 9).
  • A sign-in that resists guessing. A client that floods the sign-in with requests is answered 429 Too Many Requests before a password is even checked; the limit is keyed on the connection's address, never on a header a client sends.
  • HTTPS from the first boot. nginx serves the listing on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
  • The served folder is locked down. Directory Lister relies on PHP's open_basedir and its hidden file rules to keep visitors inside the folder it serves. This image sets the folder as PHP's hard limit, keeps the application itself in a hidden folder inside it, and hides every dot file and dot folder. Paths that climb out of the folder (../, encoded or absolute), symbolic links that point outside it, the application's code, configuration and cache, and any hidden file are refused through every listing, download, search, zip and file info request.
  • Nothing in the folder ever runs. nginx runs PHP only as Directory Lister's own front controller and serves only its compiled stylesheets, scripts, fonts and icons; a PHP file you put in the folder is offered as a download, never executed. Files opened from the listing carry a policy that stops an HTML or SVG file from running script in the site, and the listing page itself allows only its own scripts.
  • Fail closed. nginx and PHP refuse to start until first boot has finished.
  • Hardened defaults. The code and your files are owned by root; PHP runs as its own unprivileged user, can write only Directory Lister's cache, cannot run shell commands or open URLs, and never sees the sign-in password. nginx and PHP are blocked from the instance metadata service, the AMI requires IMDSv2, and requests for a host name the instance does not serve are dropped.
  • Fully patched Ubuntu 24.04 LTS at build time, including Ubuntu's PHP 8.3 fix for the PHP2Shell vulnerabilities (CVE-2026-109618 and CVE-2026-109619), with unattended security upgrades switched on.

What is on the image: Directory Lister 5.8.1 (the official release archive, checksum verified), PHP 8.3 FPM, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. A small sample folder (a README, documents, data and images) shows how the listing looks until you add your own files.

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 (the listing) and TCP 80 (redirects to HTTPS) from the networks that should reach them. Keep 22 to your own networks.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Directory Lister 5.8.1 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (Directory Lister is very light; a smaller instance is fine for a small listing), your key pair and a security group as above. Give the instance enough disk for the files you will serve, or attach a separate EBS volume for them (Step 7).

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

Step 2: Connect to your instance

Version SSH user
Directory Lister 5.8.1 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot takes a few seconds. Then nginx, PHP and the first boot unit are active:

systemctl is-active nginx php8.3-fpm directory-lister-firstboot
active
active
active

nginx serves ports 80 and 443; PHP listens on a local socket only, and there is no database, so the only open ports are 22, 80 and 443:

sudo ss -tlnp | awk 'NR==1 || /nginx|sshd|php/' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'
State Local Peer
LISTEN 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
LISTEN 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
LISTEN 0.0.0.0:22 users:(("sshd"),("systemd"))
LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx"))
LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx"))
LISTEN [::]:22 users:(("sshd"),("systemd"))
grep -m1 -oE "'pretty_version' => '[0-9.]+'" /var/www/directory-lister/files/.directory-lister/app/vendor/composer/installed.php
php -v | head -1
'pretty_version' => '5.8.1'
PHP 8.3.6 (cli) (built: Oct 19 2026 15:16:58) (NTS)

Step 4: Get your sign-in details

The credentials file holds the address the listing is served as (your instance's public DNS name by default), the user name and the password generated on this instance. Show it with:

sudo cat /root/directory-lister-credentials.txt
DIRECTORY_LISTER_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
DIRECTORY_LISTER_USER=admin
DIRECTORY_LISTER_PASSWORD=<24 letters and digits>

Step 5: Open the listing

Open DIRECTORY_LISTER_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 browser asks for a user name and password: sign in as admin with DIRECTORY_LISTER_PASSWORD. Your browser remembers the sign-in until you close it.

The home page lists the served folder, with its README rendered underneath:

The Directory Lister home page listing the data, documents and images folders and README.md, with the Welcome to Directory Lister README rendered below and the search box and theme switch in the header

Step 6: Browse, search and download

Click a folder to open it; the breadcrumb at the top takes you back. Click a file to open or download it, and use the download arrow at the top right to download the folder you are in as a zip archive.

The documents folder in Directory Lister with the parent link, release-notes.md and welcome.txt, and the breadcrumb Home / documents

Type in the search box to find files by name anywhere in the served folder:

Directory Lister search results for example, listing data/example.csv and images/example.svg

Hover over a file and click the information icon to see its MD5, SHA-1 and SHA-256 hashes, so whoever downloads it can check the copy they received:

The Directory Lister file information window for welcome.txt showing its md5, sha1 and sha256 hashes

The light bulb in the header switches between the light and dark themes.

Step 7: Serve your own files

The served folder is /var/www/directory-lister/files. Copy your files into it over SSH and they appear in the listing at once; there is nothing to restart. Remove the sample files you do not want:

sudo rm -r /var/www/directory-lister/files/documents /var/www/directory-lister/files/data /var/www/directory-lister/files/images
scp -i <your-key.pem> -r <your-folder> ubuntu@<public-ip>:/tmp/upload
sudo cp -r /tmp/upload/. /var/www/directory-lister/files/ && rm -rf /tmp/upload

Files must be readable by everyone on the instance (mode 644 for files and 755 for folders, which cp gives you by default) because PHP reads them as its own unprivileged user. Edit /var/www/directory-lister/files/README.md (or delete it) to change the text under the listing; any folder can have its own README.md.

Three rules keep the folder safe:

  • Keep the hidden .directory-lister folder. It holds the application and must stay inside the served folder; do not delete it, and do not point FILES_PATH anywhere else. PHP is confined to the served folder, which is what stops visitors climbing out of it.
  • Dot files are never shown. Any file or folder whose name starts with a dot is hidden from the listing, search, zips and downloads.
  • Use a mount, not a symbolic link, to serve another disk. A symbolic link that points outside the served folder is refused, and the folder that contains it cannot be listed. To serve a separate EBS volume, mount it on a folder inside the served folder, for example sudo mkdir /var/www/directory-lister/files/shared && sudo mount /dev/nvme1n1 /var/www/directory-lister/files/shared (add it to /etc/fstab by UUID to keep it across reboots).

Step 8: Listing settings

Directory Lister reads its settings from /var/www/directory-lister/files/.directory-lister/.env. Change the page title (SITE_TITLE), the sort order (SORT_ORDER, REVERSE_SORT), whether READMEs are shown (DISPLAY_READMES, READMES_FIRST) and whether folders can be downloaded as zips (ZIP_DOWNLOADS), then restart PHP so it reads the new values:

sudo nano /var/www/directory-lister/files/.directory-lister/.env
sudo systemctl restart php8.3-fpm

Leave FILES_PATH, HIDDEN_FILES_LIST and APP_DEBUG=false as they are. Directory Lister's documentation at docs.directorylister.com lists every setting.

Step 9: Private or public

The listing is private: every visitor needs the admin password. If you want to publish the folder to anyone who can reach port 443, make it public deliberately, and make it private again the same way:

sudo directory-lister-set-access public
sudo directory-lister-set-access private
sudo directory-lister-set-access status

Only make it public for files you mean to publish to the world. Keeping it private and restricting port 443 in the security group to your own networks is safer still.

Step 10: Security checks

Port 80 sends browsers to HTTPS, the listing asks for the password, the health endpoint answers for load balancer checks, and requests for a host name the instance does not serve are dropped:

H=$(sed -n 's/^set \$dl_host "\(.*\)";$/\1/p' /etc/nginx/directory-lister-host.conf)
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'no password: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/healthz"
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/
no password: 401
ok
unknown host name: 000

(000 means nginx closed the connection without an answer.) Signed in, a folder opens, while paths out of the folder, the application's own files and the hidden folder are refused. This block hands the password to curl from the credentials file without printing it:

H=$(sed -n 's/^set \$dl_host "\(.*\)";$/\1/p' /etc/nginx/directory-lister-host.conf)
for q in "dir=documents" "file=../../../../etc/passwd" "file=..%2F..%2F..%2Fetc%2Fpasswd" "file=.directory-lister/.env" "dir=.directory-lister" "zip=.."; do
  curl -sk -K <(sudo sed -n 's/^DIRECTORY_LISTER_PASSWORD=\(.*\)/user = "admin:\1"/p' /root/directory-lister-credentials.txt) \
    -o /dev/null -w "?$q: %{http_code}\n" --resolve "$H:443:127.0.0.1" "https://$H/?$q"
done
?dir=documents: 200
?file=../../../../etc/passwd: 404
?file=..%2F..%2F..%2Fetc%2Fpasswd: 404
?file=.directory-lister/.env: 404
?dir=.directory-lister: 404
?zip=..: 404

The credentials file is readable by root only, the password hash file by root and nginx only, the served folder belongs to root and PHP can write only the application's cache. The hash file holds one user, admin, with a bcrypt hash:

sudo stat -c '%a %U:%G %n' /root/directory-lister-credentials.txt /etc/nginx/directory-lister.htpasswd /var/www/directory-lister/files /var/www/directory-lister/files/.directory-lister/app/cache
sudo cut -c1-13 /etc/nginx/directory-lister.htpasswd
grep auth_basic /etc/nginx/directory-lister-access.conf
600 root:root /root/directory-lister-credentials.txt
640 root:www-data /etc/nginx/directory-lister.htpasswd
755 root:root /var/www/directory-lister/files
750 dirlister:dirlister /var/www/directory-lister/files/.directory-lister/app/cache
admin:$2y$10$
auth_basic "Directory Lister";
auth_basic_user_file /etc/nginx/directory-lister.htpasswd;

nginx and PHP cannot reach the instance metadata service, and PHP runs as dirlister, confined to the served folder, with no shell commands and no URL access:

systemctl show nginx -p IPAddressDeny
systemctl show php8.3-fpm -p IPAddressDeny
grep -E '^(user|php_admin_value\[open_basedir\]|php_admin_value\[disable_functions\]|php_admin_flag\[allow_url_fopen\])' /etc/php/8.3/fpm/pool.d/dirlister.conf
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
user = dirlister
php_admin_value[open_basedir] = /var/www/directory-lister/files
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec
php_admin_flag[allow_url_fopen] = off

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

H=$(sed -n 's/^set \$dl_host "\(.*\)";$/\1/p' /etc/nginx/directory-lister-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

Step 11: Your own DNS name and certificate

Point a DNS record at the instance, then set the name the listing is served as. This mints a new self signed certificate for the name, updates nginx and the URL in /root/directory-lister-credentials.txt, and keeps the name across reboots:

sudo directory-lister-set-hostname <your-domain>

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

sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/directory-lister.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/directory-lister.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-directory-lister-headers.conf. The image does not send one: browsers ignore HSTS from a site whose certificate they do not trust.

Step 12: Password and upgrades

directory-lister-set-password gives admin a new random password, writes its bcrypt hash for nginx and records it in /root/directory-lister-credentials.txt; browsers that saved the old one are asked to sign in again. Run it without arguments, or pass your own password (24 to 64 letters and digits) on stdin:

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

If you lock yourself out with too many fast requests, wait a few seconds: the limit clears itself.

To upgrade to a later Directory Lister release, snapshot the instance, download the release archive from the Directory Lister releases page, and replace the contents of /var/www/directory-lister/files/.directory-lister with it, keeping .env and hidden-files.txt, emptying app/cache and leaving the tree owned by root (with app/cache owned by dirlister). Your files are untouched, because they live outside the application folder.

Step 13: Server components

Component Path
Served folder (your files) /var/www/directory-lister/files
Directory Lister (hidden, root owned) /var/www/directory-lister/files/.directory-lister
Settings /var/www/directory-lister/files/.directory-lister/.env
nginx site /etc/nginx/sites-available/cloudimg-directory-lister
Sign-in switch and password hash /etc/nginx/directory-lister-access.conf, /etc/nginx/directory-lister.htpasswd
PHP pool /etc/php/8.3/fpm/pool.d/dirlister.conf
Certificate and key /etc/nginx/ssl/
First boot script /usr/local/sbin/directory-lister-firstboot.sh
Helpers /usr/local/sbin/directory-lister-set-access, /usr/local/sbin/directory-lister-set-password, /usr/local/sbin/directory-lister-set-hostname
Licence and release record /usr/share/doc/directory-lister/
Credentials /root/directory-lister-credentials.txt (mode 0600)

Step 14: Security recommendations

  • Allow port 22 in the security group from your own networks only, and port 443 from the networks that should see the files.
  • Keep the listing private unless you mean to publish every file in it, and keep the password secret (rotate it with Step 12 if it may have been shared).
  • Use your own DNS name and a certificate your browsers trust (Step 11).
  • If you add a .customizations.html file (Directory Lister's way to add analytics or custom scripts), note that the listing page only runs its own scripts; extend the Content-Security-Policy in /etc/nginx/sites-available/cloudimg-directory-lister for the sources you add.
  • Keep the operating system patched; unattended security upgrades are on, and sudo apt-get update && sudo apt-get upgrade applies everything else. PHP-FPM and the PHP zip extension (with its libzip library) 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).
  • Back up your files with EBS snapshots of the instance's volumes.

Support and licensing

Directory Lister is open source under the MIT licence (a copy is at /usr/share/doc/directory-lister/LICENSE); there is no per user or per CPU licence fee. The application is the unmodified upstream release; cloudimg's configuration surrounds it. 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