Storage AWS

HomeGallery 1.21.0 on AWS User Guide

| Product: HomeGallery 1.21.0 on Ubuntu 24.04 on AWS

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 HomeGallery gallery grid with the Show All, Years, Videos, Edit, Tags and Map menu and the search box, showing synthetic test images and two videos with their running time

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).

A single image opened in HomeGallery with the Media Details panel showing its file name, resolution, date, folder, GPS position, camera and the Edit tags and Share media buttons

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

The HomeGallery Years page listing 2026, 2025 and 2024 with the number of images and videos in each year

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

The HomeGallery Map page showing image thumbnails placed on a world map at London, Paris, New York, Tokyo and Sydney

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:

HomeGallery search results for the query year = 2025, showing the five images taken in 2025

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 under apiServer: disable: (or point extractor.apiServer.url at your own HomeGallery API server).
  • Place names (reverse geocoding) send each photo's GPS position to nominatim.openstreetmap.org. To enable it, delete the geoAddressExtractor line under pluginManager: 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 upgrade applies 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