PiGallery2 3.5.2
Overview
PiGallery2 is an open source, self-hosted photo and video gallery. It takes a folder of photos and videos, keeps your folder structure, indexes the files into a small SQLite database and makes thumbnails. It serves a fast web gallery with search, a map of geotagged photos, faces, albums and password-protected sharing links. Your photos stay on your own instance.
This image runs PiGallery2 3.5.2 on Ubuntu 24.04 on Amazon EC2. It is one version of the SecureAnyCloud File Sharing and Sync listing on AWS Marketplace, and you pick it as the software version when you subscribe and launch.
What you get
- HTTPS and a unique admin password from the first boot. PiGallery2 itself listens on 127.0.0.1 only. nginx serves it on port 443 with a certificate generated on your instance. Port 80 redirects to HTTPS, and requests for a host name the instance does not serve get no answer. PiGallery2's own login is on. The
adminpassword is generated on first boot and written to/root/pigallery2-credentials.txt(readable by root only), and PiGallery2 stores only a bcrypt hash of it. The upstream fallback loginadmin/adminis never created, and nothing secret is baked into the image. - The empty-password login fix. In PiGallery2 3.5.2, a login with a known user name and an empty password is accepted. The PiGallery2 developers fixed this in their code in May 2026, but no release contains the fix yet. This image applies that fix (upstream commit bf910bed3) to 3.5.2, and its self-test proves that empty and malformed passwords are refused.
- A current runtime and refreshed dependencies. PiGallery2 runs on Node.js 22.23.3 LTS, with image processing by sharp 0.35.5 (libvips 8.18) and video thumbnails by Ubuntu's ffmpeg, which apt keeps current. The libraries that PiGallery2 3.5.2 pinned carried known high and critical vulnerabilities. They are updated here, and the finished tree is scanned against the OSV vulnerability database, with no known advisories at build time. The build record is in
/usr/share/doc/pigallery2/BUILD-PROVENANCE.txt. - Hardened defaults. PiGallery2 runs as its own unprivileged user under a locked-down systemd unit. PiGallery2 and nginx are blocked from the instance metadata service, and the AMI requires IMDSv2. Client addresses come from the connection, not from headers a client can forge. Repeated sign-in attempts are rate limited, and a state-changing browser request that comes from another web site is refused. Extensions (third-party server code) and browser uploads are off until you turn them on.
- A dedicated data volume. Your photos, the index, the thumbnails and PiGallery2's configuration live on their own 20 GiB EBS volume mounted at
/var/lib/pigallery2, which you can grow on its own. - No sample content. The gallery starts empty, and you add your own photos.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
- 24/7 SecureAnyCloud support.
What is on the image: PiGallery2 3.5.2 (/opt/pigallery2/app), Node.js 22.23.3 (/opt/pigallery2/node), nginx, ffmpeg, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
Before you deploy, make sure you have:
- 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 that you use for management.
- A security group that allows TCP 443 (the gallery) and TCP 80 (redirects to HTTPS) from the people who will use it. It should allow TCP 22 (SSH, which is also how you copy photos in) from your own networks only.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick PiGallery2 3.5.2 on Ubuntu 24.04 as the software version, choose your Region, then choose Launch through EC2.
Choose m5.large, your key pair and a security group as described above. Making thumbnails for a large library is CPU-heavy, and a bigger type indexes faster.
The AMI has two volumes: the 8 GiB root volume and a 20 GiB data volume for /var/lib/pigallery2. Size the data volume for your library plus roughly a fifth again for thumbnails, for example 200 GiB instead of 20 GiB. The first boot grows its file system to fill the volume.
To launch with the AWS CLI instead, 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 \
--block-device-mappings 'DeviceName=/dev/sdf,Ebs={VolumeSize=200,VolumeType=gp3}' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=pigallery2}]'Connect to your instance
For the PiGallery2 3.5.2 on Ubuntu 24.04 version, the SSH user is ubuntu. Connect with the key pair you chose:
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot takes a few seconds. Afterwards, PiGallery2, nginx and the first-boot unit are active:
systemctl is-active pigallery2 nginx pigallery2-firstbootactive active activenginx is the only web listener. PiGallery2 itself, a Node.js process, listens on 127.0.0.1:8080 only:
sudo ss -tlnp | awk 'NR==1 || /nginx|sshd|:8080 /' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'State Local Peer LISTEN 127.0.0.1:8080 users:(("node")) 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"))Check the runtime, the PiGallery2 release and the data volume:
/opt/pigallery2/node/bin/node --version jq -r .version /opt/pigallery2/app/package.json findmnt -no SOURCE,FSTYPE,SIZE /var/lib/pigallery2v22.23.3 3.5.2 /dev/nvme1n1 ext4 19.5GGet your URL and password
sudo cat /root/pigallery2-credentials.txtThe file holds the address that PiGallery2 is served as, which is your instance's public IP address by default. It also holds the admin password generated on this instance:
# PiGallery2 3.5.2 - generated on first boot by pigallery2-firstboot.service. Unique to this instance; keep it safe. PIGALLERY2_URL=https://<public-ip>/ PIGALLERY2_USER=admin PIGALLERY2_PASS=<24 letters and digits>
# Sign in to the gallery with this user name and password. Only a bcrypt hash of the password is stored by PiGallery2.
# Add photos and videos: copy them into /var/lib/pigallery2/images (the 'ubuntu' user can write there), then open the
# gallery (folders are indexed when you browse them; Settings -> Indexing rescans everything).
# Change the host name:
sudo pigallery2-set-host <dns-name>
# Change the password:
sudo pigallery2-set-password (prompts)Add your photos and videos
PiGallery2 reads photos and videos from /var/lib/pigallery2/images, and its folders become albums in the gallery. The ubuntu user is in the pigallery2 group, which can write there, so you can copy photos straight in over SSH. For example, run this from your own computer:
rsync -av --progress -e "ssh -i <your-key.pem>" ~/Pictures/Your-Photos/ ubuntu@<public-ip>:/var/lib/pigallery2/images/SFTP clients and aws s3 sync run on the instance work too, and so does scp -r.
Keep the hidden file .sac-gallery-root in the folder. PiGallery2 will not index an empty photo folder, which protects its index if the storage is missing. The file keeps a new gallery working and is never shown.
PiGallery2 indexes a folder the first time someone opens it, and it notices new and removed files when the folder is opened again. After a large copy, index the whole library at once:
- Sign in and open Settings from the menu at the top right.
- Switch the mode (top right) to Advanced.
- In the Indexing section, choose Run now: Indexing.
Thumbnails are made as you browse. The Photo and Video sections have jobs that make them ahead of time.
Check the photo folder and its group permissions:
ls -ld /var/lib/pigallery2/images id -nG ubuntudrwxrwsr-x 5 pigallery2 pigallery2 4096 Oct 20 20:26 /var/lib/pigallery2/images ubuntu adm cdrom
sudo dip lxd pigallery2Sign in
Open PIGALLERY2_URL in your browser. The certificate is self-signed and generated on your instance, so accept the browser warning once, or use your own name and certificate (Step 10). Sign in as admin with PIGALLERY2_PASS.
The gallery opens on your photo folder. It shows subfolders first, then the photos in the folder, grouped by date.
The images in the screenshots are generated test patterns and fractals. The image itself ships with an empty gallery.
Browse, search and share
Open a folder to see its photos and videos as a grid. The bar at the top shows where you are and how many items there are. It also lets you download, filter, sort and group the items.
Click a photo to open it full screen. The arrows move between items, the play button starts a slideshow, and the i button shows the photo's details: file, size, date and, when the photo has them, camera, lens and location.
The search box finds photos by file name, folder, date, place, camera and keywords.
- Share creates a link to the current folder for people without a login, optionally with a password and an expiry. A share link uses your configured address (Step 10).
- Albums saves searches.
- Faces lists the people tagged in your photos' metadata.
- The map shows geotagged photos. Your browser loads the map tiles from OpenStreetMap.
Settings
Signed in as admin, open Settings from the menu at the top right. Basic mode shows the main options. Advanced mode shows everything else, including indexing, thumbnail and video conversion jobs, and the user list (Add user), where you can give other people their own logins and roles.
PiGallery2 keeps its settings in /var/lib/pigallery2/config/config.json. Only the pigallery2 user can read the file, because it holds the session signing keys and the admin password hash. Check the settings that make up this image's security model:
sudo python3 -c 'import json; c=json.load(open("/var/lib/pigallery2/config/config.json")); print(json.dumps({"Server":{k:c["Server"][k] for k in ("host","port","publicUrl","trustProxy")},"Users":{"authenticationRequired":c["Users"]["authenticationRequired"]},"Extensions":{"enabled":c["Extensions"]["enabled"]},"Upload":{"enabled":c["Upload"]["enabled"]}},indent=2))'{ "Server": { "host": "127.0.0.1", "port": 8080, "publicUrl": "https://<public-ip>", "trustProxy": "loopback" }, "Users": { "authenticationRequired": true }, "Extensions": { "enabled": false }, "Upload": { "enabled": false } }Keep Host 127.0.0.1, Port 8080, Trust Proxy loopback and Password protection on. PiGallery2 must be reachable only through nginx, and with password protection off, anyone who can reach the instance gets full admin rights.
Two features are off on this image:
- Upload lets signed-in users upload photos through the browser. nginx accepts uploads of up to 1 GiB per request.
- Extensions download and run third-party server code from a repository on the internet. Extensions with dependencies need npm, which this image does not ship. Turn extensions on only for extensions you trust.
Check the security model
The image ships a self-test that proves the login over HTTPS. It confirms that the gallery refuses you without signing in, and refuses a wrong password, an empty password and admin / admin. It confirms that the gallery accepts the password from your credentials file, and that PiGallery2 listens on 127.0.0.1 only. Run it:
sudo /usr/local/sbin/pigallery2-selftestOK: https://<public-ip>/ refuses no session / wrong / empty / admin:admin, logs in with the per-instance password, app on 127.0.0.1:8080 onlyNext, check the network behaviour. Plain HTTP redirects to HTTPS. The gallery's API refuses you until you sign in. A sign-in with an empty password is rejected, and so is a sign-in posted from another web site. A request for a host name that the instance does not serve gets no answer:
H=$(sudo sed -nE 's#^PIGALLERY2_URL=https://([^/]+)/.*#\1#p' /root/pigallery2-credentials.txt)
curl -s -o /dev/null -w 'plain HTTP: %{http_code} -> %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'gallery API without signing in: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/pgapi/gallery/content/"
curl -sk -w '\n' --resolve "$H:443:127.0.0.1" -H 'Content-Type: application/json' -H "Origin: https://$H" --data '{"loginCredential":{"username":"admin","password":""}}' "https://$H/pgapi/user/login" | cut -c1-80
curl -sk -o /dev/null -w 'sign in posted from another web site: %{http_code}\n' --resolve "$H:443:127.0.0.1" -H 'Origin: https://evil.example' -H 'Content-Type: application/json' --data '{}' "https://$H/pgapi/user/login"
curl -s -o /dev/null -w 'unknown host name: %{http_code}\n' -H 'Host: evil.example' http://127.0.0.1/ || trueplain HTTP: 301 -> https://<public-ip>/ gallery API without signing in: 401 {"error":{"code":50,"message":"not all parameters are included for loginCredenti sign in posted from another web site: 403 unknown host name: 000Then check the isolation. PiGallery2 and nginx cannot reach the instance metadata service. nginx hands PiGallery2 the configured host and overwrites the client address with the address of the connection. Only root can read the secrets, except that PiGallery2 can also read its own configuration:
systemctl show pigallery2 nginx -p Id -p IPAddressDeny grep -E 'proxy_set_header (Host|X-Real-IP|X-Forwarded-For) ' /etc/nginx/pigallery2-proxy.conf
sudo stat -c '%a %U:%G %n' /root/pigallery2-credentials.txt /var/lib/pigallery2/config/config.json /etc/nginx/ssl/pigallery2.keyIPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 Id=pigallery2.service IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 Id=nginx.service proxy_set_header Host $pg_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; 600 root:root /root/pigallery2-credentials.txt 600 pigallery2:pigallery2 /var/lib/pigallery2/config/config.json 600 root:root /etc/nginx/ssl/pigallery2.keyFinally, check the certificate. It was generated on this instance and names its own addresses:
H=$(sudo sed -nE 's#^PIGALLERY2_URL=https://([^/]+)/.*#\1#p' /root/pigallery2-credentials.txt)
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltNamesubject=CN = <public-ip>, O = sac X509v3 Subject Alternative Name: IP Address:<public-ip>, DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhostYour own DNS name and certificate
Point a DNS record at the instance, then set the name that PiGallery2 is served as. This command creates a new self-signed certificate for the name and updates nginx. It also updates PiGallery2's public URL (used in share links) and the URL in /root/pigallery2-credentials.txt, restarts PiGallery2 and keeps the name across reboots:
sudo pigallery2-set-host <your-domain>To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running pigallery2-set-host again replaces it with a self-signed certificate:
sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/pigallery2.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/pigallery2.key
sudo nginx -t && sudo systemctl reload nginxChange the password
Run the helper on the instance. It prompts for the new password twice, so the password never appears on a command line. It stores the password as a bcrypt hash in PiGallery2's database and configuration, updates PIGALLERY2_PASS in /root/pigallery2-credentials.txt and restarts PiGallery2:
sudo pigallery2-set-passwordThe helper also resets a forgotten password. To give other people their own logins, use Add user under Settings (Advanced mode, Users). Each user signs in with their own password and role.
Storage, backups and server components
To grow the data volume, modify the EBS volume in the console or CLI, then grow the file system on the instance. The volume is mounted by UUID with nofail:
sudo resize2fs "$(findmnt -no SOURCE /var/lib/pigallery2)"Your originals are the photo folder. PiGallery2 rebuilds the index, thumbnails and converted videos when they are missing. Your albums, shares and users live in the database. Back up /var/lib/pigallery2/images, /var/lib/pigallery2/db and /var/lib/pigallery2/config, or snapshot the data volume.
Component paths
PiGallery2 3.5.2 /opt/pigallery2/app
Node.js 22.23.3 runtime /opt/pigallery2/node/bin/node
Data volume /var/lib/pigallery2 (20 GiB gp3, ext4)
Your photos and videos /var/lib/pigallery2/images
Configuration /var/lib/pigallery2/config/config.json (pigallery2, 0600)
Index database (SQLite) /var/lib/pigallery2/db
Thumbnails and converted videos /var/lib/pigallery2/tmp
systemd unit pigallery2.service (user pigallery2, 127.0.0.1:8080)
nginx site /etc/nginx/sites-available/sac-pigallery2
Certificate and key /etc/nginx/ssl/
First boot script /usr/local/sbin/pigallery2-firstboot.sh
Host name helper /usr/local/sbin/pigallery2-set-host
Password helper /usr/local/sbin/pigallery2-set-password
Self test /usr/local/sbin/pigallery2-selftest
Credentials /root/pigallery2-credentials.txt (mode 0600)
Licences, build record, dependency scan /usr/share/doc/pigallery2/
Security recommendations
- Allow port 22 in the security group from your own networks only. Open 80 and 443 only to the people who should see the gallery.
- Keep the admin password private, and change it if it may have leaked (Step 11). Give other people their own users rather than the admin login.
- Use your own DNS name and a certificate that your users trust (Step 10), so that nobody learns to click through certificate warnings.
- Give share links a password and an expiry when they leave your organisation.
- Keep the operating system patched. Unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else, including ffmpeg updates.
Support and licensing
PiGallery2 is free software under the MIT licence (Copyright bpatrik). A copy is at /usr/share/doc/pigallery2/LICENSE, and Node.js's licence is at /usr/share/doc/pigallery2/NODE-LICENSE.
This image runs the PiGallery2 3.5.2 release from https://github.com/bpatrik/pigallery2, with the runtime, dependency and security changes listed in /usr/share/doc/pigallery2/BUILD-PROVENANCE.txt. There is no per-user or per-CPU licence fee. The AMI price covers SecureAnyCloud's hardening, first-boot setup and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24 hour response SLA
Deploy on AWS
Launch PiGallery2 3.5.2 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com