Deployment guide

HomeGallery 1.21.0

Overview

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.

This guide covers launching and using the HomeGallery 1.21.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud File Sharing and Sync listing on AWS Marketplace. You select it as the software version when you subscribe and launch.

What you get:

  • HTTPS and a password from 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. First boot generates the password and writes it 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 and 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, and the unused Chromecast component and its vulnerable libraries are removed. Two cross-site scripting issues in the photo pages are fixed (one backported from HomeGallery's unreleased code, one fixed by SecureAnyCloud). 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, and 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, not from headers a client can forge, and a browser request that changes something and comes from another website is refused.
  • No sample content. The gallery starts empty, and you add your own media.
  • A fully patched Ubuntu 24.04 LTS base at build time, with unattended security upgrades switched on.
  • 24/7 SecureAnyCloud support

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

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Select HomeGallery 1.21.0 on Ubuntu 24.04 as the software version, choose your Region, and select Launch through EC2. Choose m5.large (importing large libraries is CPU and memory heavy, and a bigger type imports faster), your key pair, and a security group as described 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 more 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).

To launch with the AWS CLI instead, run the following command and 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}]'
2

Connect to your instance

Connect over SSH as the user shown below.

Version SSH user

HomeGallery 1.21.0 on Ubuntu 24.04 ubuntu

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

First boot takes a few seconds. After it finishes, 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.

4

Get your URL and password

sudo cat /root/home-gallery-credentials.txt

The file holds the address that 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)
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. For example, from your own computer, run:

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. Subfolders are fine. HomeGallery watches the folder, and it indexes, previews and adds new files to the gallery automatically, in batches, starting a few seconds after they arrive. A large first import takes a while because HomeGallery creates previews and converts video for every file. You can follow the progress:

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

HomeGallery shows files that share a name in one folder as one item. For example, IMG_0001.HEIC and IMG_0001.MOV from a phone's live photo appear as a single item.

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
6

Sign in

Open homegallery.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). The browser asks for a user name and password. Enter admin and homegallery.pass. The gallery opens on the newest media, and you can scroll to browse everything:

The images in these screenshots are test patterns and fractals. The image ships with an empty gallery.

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. Your browser loads the map tiles from OpenStreetMap:

The search box accepts 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:

8

Optional features that send data off the instance

HomeGallery's configuration is /var/lib/home-gallery/config/gallery.config.yml. The file ships as follows, and 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 them, delete the geoAddressExtractor line under pluginManager: disabled:.

Edit the file as root, then restart HomeGallery so that the next import uses the new settings. Media that is 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, because HomeGallery must be reachable only through nginx.

9

Check the security model

The image ships a self-test that verifies the password over HTTPS. The gallery refuses requests without a password or with a wrong one, and accepts the right one. 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 requests without the password, even when a client pretends to come from the instance itself. A change posted from another website 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 only by root (or by nginx, for the password hash):

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 is 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 = 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:localhost
10

Your own DNS name and certificate

Point a DNS record at the instance, then set the name that HomeGallery is served as. The command generates a new self-signed certificate for the name and updates nginx, HomeGallery's publicUrl (used in share links) and the URL in /root/home-gallery-credentials.txt. It then 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 certificate:

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
11

Change 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, 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, then reload nginx. htpasswd prompts for the password, and the helper above changes only admin:

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.

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. A rescan rebuilds everything else, which is /var/lib/home-gallery/config (the database, file index and your tag edits) and /var/lib/home-gallery/cache (the previews). The exception is 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.

The following table lists the key components and their paths.

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/sac-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.

13

Security recommendations

  • Allow port 22 in the security group from your own networks only, and open ports 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 that your users trust (Step 10), so that nobody learns to click through certificate warnings.
  • Turn on the optional online features (Step 8) only if you accept that previews or locations 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 is 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 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 HomeGallery 1.21.0 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

Deploy on AWS