-------------------------------------------
* **ente**
* **Jonathan Haack**
* **Haack's Networking**
* **webmaster@haacksnetworking.org**
-------------------------------------------
//Ente//
-------------------------------------------
This tutorial is for Debian users who want to setup Ente manually. I used this [[https://ente.com/help/self-hosting/installation/manual|tutorial]] as a jumping off point and setup Ente as documented below. Since minio is now paywalled, I used Garage as the s3-compat bucket and just pointed it at the vm's virtiofs mountpoint which points to underlying btrfs r10 on the host (encrypted w/ LUKS).
Go / Node / Rust:
* Go: ''apt install golang-go''
* Node/npm: ''apt install nodejs npm''
* Rust: ''apt install rustup'' then ''rustup default stable''
Addresses:
* IPv4: ''8.28.86.133''
* IPv6: ''2604:fa40:0:10::36''
* Apex: ''gnulinux.pics''
DNS A/AAAA for:
* gnulinux.pics
* api.gnulinux.pics
* s3.gnulinux.pics
* albums.gnulinux.pics
* accounts.gnulinux.pics
* auth.gnulinux.pics
* share.gnulinux.pics
* cast.gnulinux.pics
* embed.gnulinux.pics
* memories.gnulinux.pics
* legacy.gnulinux.pics
Setup base system,
sudo apt update && sudo apt upgrade -y
sudo apt install -y ca-certificates curl wget git gnupg lsb-release build-essential pkg-config postgresql postgresql-contrib libsodium23 libsodium-dev caddy ufw
sudo systemctl enable --now postgresql
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
git clone https://github.com/ente-io/ente.git /root/ente
Now, Go, Rust, and Node setup:
sudo apt install -y golang-go rustup nodejs npm
rustup default stable
go version
rustc --version
node -v
npm -v
Setup PostgreSQL and create the db with ''sudo -u postgres psql'' and something like:
CREATE USER ente WITH ENCRYPTED PASSWORD 'ENTE_DB_PASS';
CREATE DATABASE ente_db OWNER ente;
GRANT ALL PRIVILEGES ON DATABASE ente_db TO ente;
\q
Next, lock access to db:
echo 'host ente_db ente 127.0.0.1/32 scram-sha-256' | sudo tee -a /etc/postgresql/*/main/pg_hba.conf
echo 'host ente_db ente ::1/128 scram-sha-256' | sudo tee -a /etc/postgresql/*/main/pg_hba.conf
sudo systemctl reload postgresql
PGPASSWORD='ENTE_DB_PASS' psql -h 127.0.0.1 -U ente -d ente_db -c 'SELECT 1;'
Edit museum.yaml and configure it, build its web apps, start it, and create unit:
cd /root/ente/server
go mod tidy
go build cmd/museum/main.go
go run tools/gen-random-keys/main.go
cp config/example.yaml museum.yaml
Edit ''/root/ente/server/museum.yaml'' using keys from second to last step above. Now, create systemd unit.
sudo tee /etc/systemd/system/ente-museum.service >/dev/null << 'EOF'
[Unit]
Description=Ente Museum
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=/root/ente/server
ExecStart=/root/ente/server/main
Restart=on-failure
RestartSec=3
Environment=GIN_MODE=release
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now ente-museum
curl -sS http://127.0.0.1:8080/ping
Build the Web apps and migrating content from repo
cd /root/ente/web
npm ci
export NEXT_PUBLIC_ENTE_ENDPOINT=https://api.gnulinux.pics
npm run build
npm run build:albums
npm run build:accounts
npm run build:auth
npm run build:cast
npm run build:share
npm run build:embed
npm run build:memories
sudo mkdir -p /var/www/ente/apps
sudo rm -rf /var/www/ente/apps/*
sudo cp -a apps/photos/out /var/www/ente/apps/photos
sudo cp -a apps/albums/out /var/www/ente/apps/albums
sudo cp -a apps/accounts/out /var/www/ente/apps/accounts
sudo cp -a apps/auth/out /var/www/ente/apps/auth
sudo cp -a apps/cast/out /var/www/ente/apps/cast
sudo cp -a apps/share/out /var/www/ente/apps/share
sudo cp -a apps/embed/out /var/www/ente/apps/embed
sudo cp -a apps/memories/out /var/www/ente/apps/memories
sudo chown -R caddy:caddy /var/www/ente
Setup Caddy reverse proxy / load balancer, acme certs, and vhosts with ''nano /etc/caddy/Caddyfile'' and enter the following:
{
email admin@gnulinux.pics
}
api.gnulinux.pics {
reverse_proxy 127.0.0.1:8080
}
gnulinux.pics {
root * /var/www/ente/apps/photos
encode gzip
file_server
try_files {path} {path}.html /index.html
}
albums.gnulinux.pics {
root * /var/www/ente/apps/albums
encode gzip
file_server
try_files {path} {path}.html /index.html
}
accounts.gnulinux.pics {
root * /var/www/ente/apps/accounts
encode gzip
file_server
try_files {path} {path}.html /index.html
}
auth.gnulinux.pics {
root * /var/www/ente/apps/auth
encode gzip
file_server
try_files {path} {path}.html /index.html
}
share.gnulinux.pics {
root * /var/www/ente/apps/share
encode gzip
file_server
try_files {path} {path}.html /index.html
}
cast.gnulinux.pics {
root * /var/www/ente/apps/cast
encode gzip
file_server
try_files {path} {path}.html /index.html
}
embed.gnulinux.pics {
root * /var/www/ente/apps/embed
encode gzip
file_server
try_files {path} {path}.html /index.html
}
memories.gnulinux.pics {
root * /var/www/ente/apps/memories
encode gzip
file_server
try_files {path} {path}.html /index.html
}
legacy.gnulinux.pics {
root * /var/www/ente/apps/accounts
encode gzip
file_server
try_files {path} {path}.html /index.html
}
s3.gnulinux.pics {
header Access-Control-Allow-Origin *
header Access-Control-Allow-Methods "GET, PUT, POST, HEAD, DELETE, OPTIONS"
header Access-Control-Allow-Headers *
header Access-Control-Expose-Headers "ETag, Accept-Ranges, Content-Range, Content-Length, Content-Type"
header Access-Control-Max-Age 3600
@options method OPTIONS
handle @options {
respond 204
}
reverse_proxy 127.0.0.1:3900
}
Verify after making config, then restart service, debug as needed:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl enable --now caddy
sudo systemctl reload caddy
curl -sS http://127.0.0.1:8080/ping
curl -sSI https://gnulinux.pics
curl -sS https://api.gnulinux.pics/ping
End point works now, go to url in browser and create first user then use the id# to lock it as admin.
- Open https://gnulinux.pics and sign up.
- OTP: ''journalctl -u ente-museum -n 200 --no-pager''
- User id: 34982349839489320843
You need to locate the id# by searching logs for your email in close proximity as per above. Or, alternately, make a one-liner:
journalctl -u ente-museum --no-pager | grep -F -A 80 'user@example.com' | grep -oE 'user_id=[1-9][0-9]*' | tail -1
Once that's done, go edit ''nano /root/ente/server/museum.yaml'':
internal:
admins:
- USER_ID
trusted-client-ip-header: X-Forwarded-For
Restart service,
sudo systemctl restart ente-museum
How to edit admin user to give them more storage:
PGPASSWORD='ENTE_DB_PASS' psql -h 127.0.0.1 -U ente -d ente_db -c "UPDATE subscriptions SET storage = 25::bigint * 1024 * 1024 * 1024 WHERE user_id = USER_ID RETURNING user_id, storage;"
# 100 250 500 750 → same, change 25
# 1TB = 1024 1.5TB = 1536 2TB = 2048
## 8. Garage (needed before uploads work)
Warning: If you rerun layout/bucket/key it hoses the current bucket.
sudo mkdir -p /var/lib/garage/meta /mnt/warehouse/media/glpics
curl -fL -o /tmp/garage https://garagehq.deuxfleurs.fr/_releases/v2.4.1/x86_64-unknown-linux-musl/garage
sudo install -m 0755 /tmp/garage /usr/local/bin/garage
garage --version
Create config .toml file and start garage service:
sudo tee /etc/garage.toml >/dev/null << 'EOF'
metadata_dir = "/var/lib/garage/meta"
data_dir = "/mnt/warehouse/media/glpics"
db_engine = "sqlite"
replication_factor = 1
rpc_bind_addr = "127.0.0.1:3901"
rpc_public_addr = "127.0.0.1:3901"
rpc_secret = "GARAGE_RPC_SECRET"
[s3_api]
s3_region = "garage"
api_bind_addr = "127.0.0.1:3900"
root_domain = ".s3.gnulinux.pics"
[admin]
api_bind_addr = "127.0.0.1:3903"
admin_token = "GARAGE_ADMIN_TOKEN"
EOF
Create systemd unit for garage to monitor and restart service:
sudo tee /etc/systemd/system/garage.service >/dev/null << 'EOF'
[Unit]
Description=Garage S3
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/garage -c /etc/garage.toml server
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now garage
sudo systemctl status garage --no-pager
Create bucket on the same host and name it / give perms for Ente's default s3-compatible endpoint:
garage -c /etc/garage.toml status
NODE=$(garage -c /etc/garage.toml node id -q | head -1)
garage -c /etc/garage.toml layout assign -z dc1 -c 1T "$NODE"
garage -c /etc/garage.toml layout apply --version 1
garage -c /etc/garage.toml bucket create b2-eu-cen
garage -c /etc/garage.toml key create ente-photos
garage -c /etc/garage.toml bucket allow b2-eu-cen --read --write --owner --key ente-photos
garage -c /etc/garage.toml key info ente-photos
Once the bucket is built, add the creds it spit out into ''/root/ente/server/museum.yaml'' and restart Museum.
s3:
are_local_buckets: false
use_path_style_urls: true
b2-eu-cen:
key: GARAGE_ACCESS_KEY
secret: GARAGE_SECRET_KEY
endpoint: s3.gnulinux.pics
region: garage
bucket: b2-eu-cen
Restart the service
sudo systemctl restart ente-museum
Draft Upgrade Garage Steps
VER=v2.4.1
ARCH=x86_64-unknown-linux-musl
curl -fL -o /tmp/garage "https://garagehq.deuxfleurs.fr/_releases/${VER}/${ARCH}/garage"
/tmp/garage --version
sudo systemctl stop ente-museum garage
sudo cp -a /usr/local/bin/garage /usr/local/bin/garage.bak-$(date +%F)
sudo install -m 0755 /tmp/garage /usr/local/bin/garage
sudo systemctl start garage ente-museum
garage -c /etc/garage.toml status
curl -sS https://api.gnulinux.pics/ping
Potential steps if garage upgrade fails:
sudo systemctl stop garage
sudo cp /usr/local/bin/garage.bak-YYYY-MM-DD /usr/local/bin/garage
sudo systemctl start garage
Do not layout/bucket/key on upgrade - it will hose current bucket. Example museum.yaml example config layout for base setup
db:
host: 127.0.0.1
port: 5432
name: ente_db
user: ente
password: ENTE_DB_PASS
s3:
are_local_buckets: false
use_path_style_urls: true
b2-eu-cen:
key: GARAGE_ACCESS_KEY
secret: GARAGE_SECRET_KEY
endpoint: s3.gnulinux.pics
region: garage
bucket: b2-eu-cen
apps:
public-albums: https://albums.gnulinux.pics
embed-albums: https://embed.gnulinux.pics
public-locker: https://share.gnulinux.pics
public-paste: https://share.gnulinux.pics
cast: https://cast.gnulinux.pics
accounts: https://accounts.gnulinux.pics
public-memories: https://memories.gnulinux.pics
legacy: https://gnulinux.pics
key:
encryption: MUSEUM_KEY_ENCRYPTION
hash: MUSEUM_KEY_HASH
jwt:
secret: MUSEUM_JWT_SECRET
internal:
admins:
- USER_ID
trusted-client-ip-header: X-Forwarded-For
smtp:
host: mail.example.com
port: 587
username: SMTP_USER
password: SMTP_PASS
email: SMTP_FROM
sender-name: gnulinux.pics
Draft update sequence
sudo apt update && sudo apt upgrade
rustup update
cd /root/ente && git pull
cd /root/ente/server
go mod tidy
go build cmd/museum/main.go
sudo systemctl restart ente-museum
cd /root/ente/web
npm ci
export NEXT_PUBLIC_ENTE_ENDPOINT=https://api.gnulinux.pics
npm run build
npm run build:albums
npm run build:accounts
npm run build:auth
npm run build:cast
npm run build:share
npm run build:embed
npm run build:memories
sudo rsync -a --delete apps/photos/out/ /var/www/ente/apps/photos/
sudo rsync -a --delete apps/albums/out/ /var/www/ente/apps/albums/
sudo rsync -a --delete apps/accounts/out/ /var/www/ente/apps/accounts/
sudo rsync -a --delete apps/auth/out/ /var/www/ente/apps/auth/
sudo rsync -a --delete apps/cast/out/ /var/www/ente/apps/cast/
sudo rsync -a --delete apps/share/out/ /var/www/ente/apps/share/
sudo rsync -a --delete apps/embed/out/ /var/www/ente/apps/embed/
sudo rsync -a --delete apps/memories/out/ /var/www/ente/apps/memories/
sudo chown -R caddy:caddy /var/www/ente
sudo systemctl reload caddy
curl -sS https://api.gnulinux.pics/ping
Store secrets properly
--- //[[alerts@haacksnetworking.org|oemb1905]] 2026/09/21 02:50//