HomeGallery 1.21.0 on AWS User Guide
Overview
This guide covers launching and using the HomeGallery 1.21.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg File Sharing and Sync listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
HomeGallery is an open source, self hosted photo and video gallery. You put your photos and videos in a folder on the instance; HomeGallery indexes them into a file based database (no database server), builds previews, reads the camera metadata and serves a fast web gallery with a timeline, a year view, a map of geotagged media, tags and a search language. Your media stays on your own instance.
The cloudimg image adds:
- HTTPS and a password from the first boot. HomeGallery itself listens on 127.0.0.1 only. nginx serves it on port 443 with a certificate generated on your instance and asks for a user name and password (HTTP Basic Auth) on every page; the password is generated on first boot and written to
/root/home-gallery-credentials.txt(readable by root only). Port 80 redirects to HTTPS, and requests for a host name the instance does not serve get no answer. Nothing secret is baked into the image. - A current runtime and patched media tooling. The upstream 1.21.0 release bundles Node.js 24.14.0 (three security releases behind), a 2018 build of ffmpeg and an older sharp/libvips. This image runs HomeGallery on Node.js 24.22.0 LTS, decodes video with Ubuntu's ffmpeg (kept current by apt) and processes images with sharp 0.35.5 (libvips 8.18). Vulnerable JavaScript dependencies are updated within their release lines, the unused Chromecast component and its vulnerable libraries are removed, and two cross site scripting issues in the photo pages are fixed (one backported from HomeGallery's unreleased code, one fixed by cloudimg). The build record is in
/usr/share/doc/home-gallery/BUILD-PROVENANCE.txt. - Private by default. Upstream HomeGallery sends every image preview to a public AI service (similar image, object and face detection) and every photo's GPS position to OpenStreetMap's geocoder. Both are switched off here; nothing leaves the instance unless you turn it on (Step 8).
- Hardened defaults. HomeGallery runs as its own unprivileged user under a locked down systemd unit that can only read your media folder. HomeGallery and nginx are blocked from the instance metadata service and the AMI requires IMDSv2. Your password never reaches HomeGallery itself, client addresses come from the connection rather than from headers a client can forge, and a browser request that changes something and comes from another web site is refused.
- No sample content. The gallery starts empty; you add your own media.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: HomeGallery 1.21.0 (/opt/home-gallery/app), Node.js 24.22.0 (/opt/home-gallery/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
- 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 443 (the gallery) and TCP 80 (redirects to HTTPS) from the people who will use it, and TCP 22 (SSH, also how you copy media in) from your own networks only.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick HomeGallery 1.21.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (importing large libraries is CPU and memory heavy; a bigger type imports faster), your key pair and a security group as above.
Your media, its previews and the gallery database all live on the instance, so give the root volume room for your library plus roughly a fifth again for previews (for example 100 GiB instead of the default 8 GiB). The root file system grows to fill the volume on first boot. For very large libraries, attach a separate EBS volume and mount it at /var/lib/home-gallery/media (Step 12).
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 \
--block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=100,VolumeType=gp3}' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=home-gallery}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| HomeGallery 1.21.0 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 HomeGallery, nginx and the first boot unit are active:
systemctl is-active home-gallery nginx home-gallery-firstboot
active
active
active
nginx is the only web listener; HomeGallery itself (a Node.js process) listens on 127.0.0.1:3000 only:
sudo ss -tlnp | awk 'NR==1 || /nginx|sshd|:3000 /' | 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 127.0.0.1:3000 users:(("MainThread"))
LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx"))
LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx"))
LISTEN [::]:22 users:(("sshd"),("systemd"))
The runtime and the HomeGallery release:
/opt/home-gallery/node/bin/node --version
sudo gallery --version
v24.22.0
1.21.0
gallery is HomeGallery's command line tool; on this image it runs as HomeGallery's own user, with the same metadata block as the service.
Step 4: Get your URL and password
sudo cat /root/home-gallery-credentials.txt
The file holds the address HomeGallery is served as (your instance's public IP address by default) and the password generated on this instance:
# HomeGallery 1.21.0 - generated on first boot by home-gallery-firstboot.service. Unique to this instance; keep it safe.
homegallery.url=https://<public-ip>/
homegallery.user=admin
homegallery.pass=<24 letters and digits>
# Your browser asks for this user name and password (HTTP Basic Auth) before the gallery opens.
# Add photos and videos: copy them into /var/lib/home-gallery/media (the 'ubuntu' user can write there);
# HomeGallery picks them up automatically. Full rescan: sudo systemctl restart home-gallery
# Change the host name: sudo home-gallery-set-host <dns-name>
# Change the password: sudo home-gallery-set-password (prompts)
Step 5: Add your photos and videos
HomeGallery reads media from /var/lib/home-gallery/media. The ubuntu user is in the homegallery group, which can write there, so you can copy media straight in over SSH. From your own computer, for example:
rsync -av --progress -e "ssh -i <your-key.pem>" ~/Pictures/Your-Photos/ ubuntu@<public-ip>:/var/lib/home-gallery/media/
(scp -r, SFTP clients and aws s3 sync run on the instance work too.) Sub folders are fine. HomeGallery watches the folder: new files are indexed, previewed and added to the gallery automatically, in batches, starting a few seconds after they arrive. A large first import takes a while (previews and video conversion for every file); you can follow it:
sudo journalctl -u home-gallery -f
To force a complete rescan (for example after moving or deleting many files), restart the service:
sudo systemctl restart home-gallery
Files that share a name in one folder (IMG_0001.HEIC and IMG_0001.MOV from a phone's live photo, for example) are shown as one item, as HomeGallery intends.
The media folder and its group permissions:
ls -ld /var/lib/home-gallery/media
id -nG ubuntu
drwxrwsr-x 2 homegallery homegallery 4096 Oct 20 17:43 /var/lib/home-gallery/media
ubuntu adm cdrom sudo dip lxd homegallery
Step 6: Sign in
Open homegallery.url in your browser. The certificate is self signed (generated on your instance), so accept the browser warning once, or use your own name and certificate (Step 10). The browser asks for a user name and password: enter admin and homegallery.pass. The gallery opens on the newest media; scroll to browse everything:

The images in these screenshots are generated test patterns and fractals; the image ships with an empty gallery.
Step 7: Browse, search and explore
Click any item to open it full screen; the arrows move between items and the i button shows its details: file, size, date, camera, location and tags. Edit tags adds your own tags. Share media creates a link to a reduced view of one item; on this image every page, share links included, asks for the gallery password, so only people you give a login to can open it (Step 11 shows how to add logins).

Years groups your library by year (with image and video counts), Videos shows only videos and Tags lists your tags:

Map places every geotagged photo on a world map (the map tiles are loaded by your browser from OpenStreetMap):

The search box takes plain words (file names, tags, places, camera models) and a query language, for example year = 2025, type:video or year >= 2025 and not type:video:

Step 8: Optional features that send data off the instance
HomeGallery's configuration is /var/lib/home-gallery/config/gallery.config.yml. As shipped (the publicUrl is your configured host):
sudo grep -v '^ *#' /var/lib/home-gallery/config/gallery.config.yml
configDir: /var/lib/home-gallery/config
cacheDir: /var/lib/home-gallery/cache
sources:
- dir: /var/lib/home-gallery/media
extractor:
apiServer:
disable:
- similarDetection
- objectDetection
- faceDetection
useNative:
- ffprobe
- ffmpeg
server:
host: 127.0.0.1
port: 3000
publicUrl: https://<public-ip>
openBrowser: false
importSources: true
watchSources: true
pluginManager:
disabled:
- geoAddressExtractor
Two upstream features are off because they send your data to services outside your instance:
- Similar images, object and face detection send a small preview of every image to HomeGallery's public API server (
api.home-gallery.org). To enable them, delete the corresponding lines underapiServer: disable:(or pointextractor.apiServer.urlat your own HomeGallery API server). - Place names (reverse geocoding) send each photo's GPS position to
nominatim.openstreetmap.org. To enable it, delete thegeoAddressExtractorline underpluginManager: disabled:.
Edit the file as root, then restart HomeGallery so the next import uses the new settings (media already imported is processed on the next full rescan):
sudo nano /var/lib/home-gallery/config/gallery.config.yml
sudo systemctl restart home-gallery
Keep server.host: 127.0.0.1: HomeGallery must only be reachable through nginx.
Step 9: Check the security model
The image ships a self test that proves the password over HTTPS: the gallery refuses you without a password and with a wrong one, accepts the right one, and HomeGallery listens on 127.0.0.1 only:
sudo /usr/local/sbin/home-gallery-selftest
OK: Basic Auth 401/401/200 on https://<public-ip>/, gallery API reachable with the password, app on 127.0.0.1:3000 only
Plain HTTP redirects to HTTPS, the gallery and its API refuse you without the password (also when a client pretends to come from the instance itself), a change posted from another web site is refused, and a request for a host name the instance does not serve gets no answer:
H=$(sudo sed -nE 's#^homegallery.url=https://([^/]+)/.*#\1#p' /root/home-gallery-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 without the password: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'forged X-Forwarded-For 127.0.0.1, no password: %{http_code}\n' --resolve "$H:443:127.0.0.1" -H 'X-Forwarded-For: 127.0.0.1' "https://$H/api/database.json"
curl -sk -o /dev/null -w 'change 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/api/events"
curl -s -o /dev/null -w 'unknown host name: %{http_code}\n' -H 'Host: evil.example' http://127.0.0.1/ || true
plain HTTP: 301 -> https://<public-ip>/
gallery without the password: 401
forged X-Forwarded-For 127.0.0.1, no password: 401
change posted from another web site: 403
unknown host name: 000
HomeGallery and nginx cannot reach the instance metadata service; nginx hands HomeGallery the configured host, never passes your password on, and overwrites the client address with the address of the connection; the secrets are readable by root (or nginx, for the password hash) only:
systemctl show home-gallery nginx -p Id -p IPAddressDeny
grep -E 'proxy_set_header (Host|Authorization|X-Real-IP|X-Forwarded-For) ' /etc/nginx/home-gallery-proxy.conf
sudo stat -c '%a %U:%G %n' /root/home-gallery-credentials.txt /etc/nginx/home-gallery.htpasswd /etc/nginx/ssl/home-gallery.key
IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128
Id=home-gallery.service
IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128
Id=nginx.service
proxy_set_header Host $hg_host;
proxy_set_header Authorization "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
600 root:root /root/home-gallery-credentials.txt
640 root:www-data /etc/nginx/home-gallery.htpasswd
600 root:root /etc/nginx/ssl/home-gallery.key
The certificate was generated on this instance and names its own addresses:
H=$(sudo sed -nE 's#^homegallery.url=https://([^/]+)/.*#\1#p' /root/home-gallery-credentials.txt)
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
subject=CN = <public-ip>, O = cloudimg
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:localhost
Step 10: Your own DNS name and certificate
Point a DNS record at the instance, then set the name HomeGallery is served as. This mints a new self signed certificate for the name, updates nginx, HomeGallery's publicUrl (used in share links) and the URL in /root/home-gallery-credentials.txt, restarts HomeGallery and keeps the name across reboots:
sudo home-gallery-set-host <your-domain>
To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx (running home-gallery-set-host again replaces it with a self signed one):
sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/home-gallery.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/home-gallery.key
sudo nginx -t && sudo systemctl reload nginx
Step 11: Change the password
Run the helper on the instance. It prompts for the new password twice (so it never appears on a command line), stores it as a bcrypt hash, updates homegallery.pass in /root/home-gallery-credentials.txt and reloads nginx; browsers ask for the new password on their next visit:
sudo home-gallery-set-password
There is one login (admin). To give other people their own logins, add them to the password file (htpasswd prompts for the password; the helper above only ever changes admin), then reload nginx:
sudo htpasswd -B /etc/nginx/home-gallery.htpasswd <user>
sudo systemctl reload nginx
Remove a login with sudo htpasswd -D /etc/nginx/home-gallery.htpasswd <user>. Every login sees the whole gallery.
Step 12: Storage, backups and server components
To keep a large library on its own disk, attach an EBS volume, format it and mount it at the media folder (by file system UUID in /etc/fstab, with nofail), then give it the folder's ownership and restart HomeGallery:
sudo chown homegallery:homegallery /var/lib/home-gallery/media && sudo chmod 2775 /var/lib/home-gallery/media
sudo systemctl restart home-gallery
Your originals are the media folder. Everything else (/var/lib/home-gallery/config with the database, file index and your tag edits, and /var/lib/home-gallery/cache with the previews) is rebuilt by a rescan, except your tag edits, which live in config/events.db: back up the media folder and /var/lib/home-gallery/config (or snapshot the EBS volumes).
| Component | Path |
|---|---|
| HomeGallery 1.21.0 | /opt/home-gallery/app |
| Node.js 24.22.0 runtime | /opt/home-gallery/node/bin/node |
| Your media (read only to HomeGallery) | /var/lib/home-gallery/media |
| Configuration, database, file index, tag edits, log | /var/lib/home-gallery/config |
| Previews and extracted metadata | /var/lib/home-gallery/cache |
| systemd unit | home-gallery.service (user homegallery, 127.0.0.1:3000) |
| nginx site | /etc/nginx/sites-available/cloudimg-home-gallery |
| Password hash | /etc/nginx/home-gallery.htpasswd (root:www-data, 0640) |
| Certificate and key | /etc/nginx/ssl/ |
| Command line tool | /usr/local/bin/gallery (run with sudo) |
| First boot script | /usr/local/sbin/home-gallery-firstboot.sh |
| Host name helper | /usr/local/sbin/home-gallery-set-host |
| Password helper | /usr/local/sbin/home-gallery-set-password |
| Self test | /usr/local/sbin/home-gallery-selftest |
| Credentials | /root/home-gallery-credentials.txt (mode 0600) |
| Licences, build record, dependency scan | /usr/share/doc/home-gallery/ |
The Chromecast command (gallery cast) is not included: it needs a Chromecast on the same local network as the server, and its libraries carried unfixed vulnerabilities.
Step 13: 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 password private and change it if it may have leaked (Step 11).
- Use your own DNS name and a certificate your users trust (Step 10), so nobody learns to click through certificate warnings.
- Only turn on the optional online features (Step 8) if you are happy for previews or locations to leave the instance.
- 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
HomeGallery is free software under the MIT licence (Copyright Sebastian Felis); a copy is at /usr/share/doc/home-gallery/LICENSE, and Node.js's licence at /usr/share/doc/home-gallery/NODE-LICENSE. This image runs the HomeGallery 1.21.0 release from https://github.com/xemle/home-gallery with the runtime, dependency and security changes listed in /usr/share/doc/home-gallery/BUILD-PROVENANCE.txt. There is no per user or per CPU licence fee. 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