The photos stay with you
EventSlide runs on a single machine: a laptop, a mini PC, a Raspberry Pi under the projector. Nothing leaves it.
EventSlide is free software: you can run it on your own machine, and your photos never leave it. This page is for people comfortable with a server. If that is not you, hosted access exists, and there is nothing to install.
See the commands Source code on GitHub
Most party photo tools ask one of two things: that your guests install an app, or that you hand hundreds of family photos to a company you have never heard of. EventSlide asks for neither.
EventSlide runs on a single machine: a laptop, a mini PC, a Raspberry Pi under the projector. Nothing leaves it.
You choose how long the album is kept, you back it up when you like, you delete it when you like. None of it depends on us.
The code is public: you can check what the software does, change it, pass it on. The published image is signed and scanned with every release.
On a machine set up in the room, uploads are queued and retried and the wall keeps playing when the network drops. It can run on its own for an eight-hour evening.
A laptop, a mini PC or a Raspberry Pi. The image exists for linux/amd64 and linux/arm64.
With docker compose. Nothing is built on your machine: the published image is simply downloaded.
It is the address the QR code holds (PUBLIC_URL). Never localhost: a QR code pointing at localhost is the most common way an install fails on the night. Scan your own QR code with your phone before the guests arrive.
EventSlide speaks plain http and does not encrypt by itself. Something has to sit in front of it: Caddy, nginx or Traefik, whichever you already use. With Caddy, it is one line of configuration.
The commands below use it to generate two random secrets. Nothing else asks for it with Docker.
This case is not covered today. A machine on a local network, with no DNS and no certificate, cannot serve https that a guest’s phone will trust, and EventSlide does not run over plain http in production. The current answer: a real domain name pointed at the machine (it works over the venue’s Wi-Fi as long as the phones have a route to it), or a --network host proxy with a certificate you already own. For a fully offline wall, say so on the repository’s issue tracker.
The image is published as ghcr.io/irony42/eventslide, for linux/amd64 and linux/arm64. It is signed and scanned with every release. The commands below are the ones in the repository’s README, word for word. When in doubt, the repository is the reference.
git clone https://github.com/Irony42/EventSlide.git
cd EventSlide
# Two secrets, and the address your guests' phones will reach.
node -e "console.log('SESSION_SECRET='+require('crypto').randomBytes(48).toString('base64url'))" >> .env
node -e "console.log('GUEST_TOKEN_SECRET='+require('crypto').randomBytes(48).toString('base64url'))" >> .env
echo "PUBLIC_URL=https://photos.example.com" >> .env
# Your own account, created on the first boot. The password is generated too, because
# one typed into a README is one everybody who read it could sign in with first.
echo "BOOTSTRAP_OWNER_EMAIL=you@example.com" >> .env
node -e "console.log('BOOTSTRAP_OWNER_PASSWORD='+require('crypto').randomBytes(18).toString('base64url'))" >> .env
docker compose up # the first time, and read what it says
docker compose up -d # once it has booted cleanlyThe node commands draw two random secrets (SESSION_SECRET, GUEST_TOKEN_SECRET), then you note the address the phones will reach (PUBLIC_URL). The two BOOTSTRAP_OWNER lines create your account on first boot: there is no default admin and no setup page. The password is generated too, because one typed into a tutorial is the one everybody would try first.
The first up pulls the published image, at the version you checked out. A missing or malformed variable makes the server print every problem at once and exit. In the background the container restarts on its own and the list scrolls away. Once it boots cleanly, -d is the right way to leave it running.
The login cookie is marked Secure: a browser never sends it back over plain http. So EventSlide refuses to start without https rather than give you a login form that never logs you in. compose.yaml binds the port to 127.0.0.1 for the proxy to reach and sets TRUST_PROXY_HOPS=1 to match. If your proxy is a container on the same Docker network, delete the ports: block and point it at eventslide:4300.
photos.example.com {
reverse_proxy 127.0.0.1:4300
}In a Caddyfile next to the compose file. Caddy and Traefik pass on what the server needs without being asked. With nginx, add proxy_set_header X-Forwarded-Proto $scheme; and proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;. Without the first, the login cookie is never sent; without the second, all your guests share the proxy’s address in the rate limiter.
Once the proxy is in place, open PUBLIC_URL/login with that email and the generated password (grep BOOTSTRAP_OWNER .env shows it). The first sign-in asks you to choose your own: at least 12 characters, and not a common one. After that, delete both BOOTSTRAP_OWNER lines or keep both: one without the other is refused.
From the checkout, instead of pulling the image:
docker compose -f compose.yaml -f compose.build.yaml up --buildYou need Node 24 or later, then the npm commands in the “Install” section of the README. Behind your https proxy, TRUST_PROXY_HOPS=1 is required: without it the sign-in form accepts your password and you stay signed out.
The docs/IMAGE-SOURCES.md file says what the image contains, where the source of each part is, and how to check the signature. Nothing is built on your machine: what you run is the published image, the one that is signed.
Take one before the event and one the morning after. The server stays up while you do. The step people skip is the one that matters: getting the archive off the machine.
# 1. Take it. It prints the name, e.g. /data/backups/eventslide-2026-09-12T08-00-00Z
docker compose exec eventslide node dist/ops/scripts/backup.js
# 2. Take it off the volume, check the copy, and only then delete it there. Until it is
# off the machine it is on the same disk as the photographs, and the command says so.
A=eventslide-2026-09-12T08-00-00Z
docker compose cp "eventslide:/data/backups/$A" . &&
docker compose run --rm -v "$PWD/$A:/restore:ro" eventslide \
node dist/ops/scripts/backup.js --verify /restore &&
docker compose exec eventslide rm -r "/data/backups/$A"The archive is a folder: a consistent snapshot of the database, every photo, and a manifest of counts and checksums. The command re-reads everything it just wrote before it says OK.
A copy on the same disk as the album does not survive that disk failing. It is a backup once a copy is on another machine, or on a drive you can unplug.
Always stop the server before restoring: restore replaces the database file, and doing that under a running server leaves a wall serving neither the old evening nor the new one. It verifies the whole archive before touching anything, and refuses to overwrite an installation without --force.
The secrets are not in the archive. Restoring photos with new secrets signs every host out.
Details, and what the checks do and do not catch, are in docs/SECURITY.md, section 11. Restoring step by step is in the “Backups” section of the README.
Run a tagged release, not the tip of the main branch (the one git clone leaves you on). Take a backup first: the migrations a new version applies when it starts only go forward, and a backup is the only way back.
git fetch --tags && git checkout vX.Y.Z
docker compose pull
docker compose up -dReplace vX.Y.Z with the version you want. What a major version may break, how a break is announced and how to go back are explained in docs/UPGRADING.md.
Here is the short version. It is not legal advice: the text of the licence is what counts.
If you modify EventSlide and other people use your version over a network, you must offer them the source code of your version, under the same licence. Running an unmodified copy adds nothing.
Every guest and host screen carries a “Source code (AGPL-3.0)” link. You can change where it points (SOURCE_CODE_URL), for a modified version for instance. No setting hides it.
Version 3.0.0 is the first under AGPL-3.0, “only”: without the “or any later version” clause. Version 2.1.0 is the last under GPL-3.0, and anything published before stays under GPL-3.0 for good.
What the licence asks of someone who runs it, changes it or forks it: docs/LICENSING-FAQ.md.
Everything is on github.com/Irony42/EventSlide. Here is where to start. The documents are in English.
The code, the published releases and the issue tracker.
What EventSlide does, install, backups, updates.
The threat model, written around who is actually in the room.
What the licence asks of someone who runs it, changes it or forks it.
What a major version may break, and how to go back.
What the image is made of, and how to check its signature.
Want to help? See CONTRIBUTING.md, or the supporting the project page.
Request access to the hosted instance at app.eventslide.fr: free, by invitation. There is nothing to install.