Installing with systemd (Ubuntu / Debian)
This page walks through building engawa from scratch on Ubuntu 24.04 LTS. Debian 12 uses the same package names, so the steps apply there almost unchanged.
If you want to use Docker, follow Installing with Docker instead. For RHEL-family systems (AlmaLinux, Rocky Linux, RHEL), see Installing with systemd (RHEL family).
Before you start
Section titled “Before you start”What you need
Section titled “What you need”| Item | Guideline |
|---|---|
| Server | 4GB of RAM or more recommended. The build step is memory-hungry |
| Domain | e.g. example.com. Point an A record (or AAAA for IPv6) at the server first |
| Mail server | Not required, but you need one to send registration confirmation mails |
| Object storage | Not required. You can configure it later from the admin panel |
The plan
Section titled “The plan”- Install the required software (Node.js, PostgreSQL, Redis)
- Create a dedicated user and a database
- Fetch the source and build it
- Write the configuration file
- Register it as a systemd service
- Put a reverse proxy (nginx) in front and serve HTTPS
Run the commands below as a regular user with sudo access.
1. Install the basics
Section titled “1. Install the basics”Bring the system up to date and install the build tools.
sudo apt updatesudo apt full-upgrade -ysudo apt install -y build-essential python3 curl git ca-certificates gnupg ffmpegbuild-essential and python3 are needed to compile some of engawa’s dependencies. Without them, pnpm install fails later on.
ffmpeg is needed to handle video files. engawa shells out to the ffmpeg and ffprobe commands directly, so they have to be present on the system. Confirm they are:
ffprobe -version2. Install Node.js
Section titled “2. Install Node.js”engawa needs Node.js 24 or newer. The version in Ubuntu’s own repository is too old, so add the NodeSource repository.
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -sudo apt install -y nodejsCheck it:
node -vA version starting with v24. means you are good.
Next, enable pnpm. engawa uses pnpm, not npm or yarn.
sudo corepack enablepnpm -v3. Install PostgreSQL
Section titled “3. Install PostgreSQL”sudo apt install -y postgresql postgresql-contribsudo systemctl enable --now postgresqlPostgreSQL 15 or newer is required; Ubuntu 24.04 gives you 16. Check with:
psql --version4. Install Redis
Section titled “4. Install Redis”sudo apt install -y redis-serversudo systemctl enable --now redis-serverVerify it is running:
redis-cli pingIt should answer PONG.
5. Create a user for engawa
Section titled “5. Create a user for engawa”Running engawa as root is best avoided, so create a dedicated user.
sudo useradd -m -s /bin/bash engawaFrom here on, anything touching engawa itself is done as this user. Switch to it with:
sudo -iu engawaType exit to return to your own user. Each step below says which user to run it as.
6. Create the database
Section titled “6. Create the database”Go back to your own user (the one with sudo) and run:
sudo -u postgres psqlAt the postgres=# prompt, run these three lines. Replace example-pass with a password of your own and keep it — you will put it in the configuration file.
CREATE ROLE engawa WITH LOGIN PASSWORD 'example-pass';CREATE DATABASE engawa OWNER engawa;\q\q leaves the prompt.
7. Fetch the source and build
Section titled “7. Fetch the source and build”From here on, work as the engawa user.
sudo -iu engawaClone the repository:
git clone --branch develop https://github.com/bettaku/engawa.git engawacd engawaInstall the dependencies. This takes a few minutes.
pnpm install --frozen-lockfile8. Write the configuration file
Section titled “8. Write the configuration file”Copy the sample and edit it, still as the engawa user.
cp .config/example.yml .config/default.ymlnano .config/default.ymlAt a minimum, adjust these:
# The public URL. The trailing slash is requiredurl: https://example.com/
# The port engawa listens on. nginx forwards to itport: 3000
db: host: localhost port: 5432 db: engawa user: engawa pass: example-pass # the password from step 6
redis: host: localhost port: 6379
# Trust the reverse proxy you set up in step 11.# Without this, every request is recorded as coming from the proxy's IPtrustProxy: - '127.0.0.1/32' - '::1/128'
# Do not change this after the server has been createdid: 'aidx'In nano, save with Ctrl+O → Enter and quit with Ctrl+X.
Every other option is documented with comments in the sample file, so it is worth reading through once. For how the file relates to environment variables, see Backend environment variables; for fine-tuning the database connection, see Options for db.extra.
9. Build and initialize the database
Section titled “9. Build and initialize the database”Still as the engawa user, from the repository root:
NODE_ENV=production pnpm buildOnce the build finishes, create the tables:
pnpm migrateThen start it by hand once to check everything works:
pnpm startA line like Now listening on port 3000 means it worked. Stop it with Ctrl+C and move on.
If it errors out, re-check the database password and the Redis settings.
10. Register it as a systemd service
Section titled “10. Register it as a systemd service”Type exit to leave the engawa user and return to your own.
Create the service file:
sudo nano /etc/systemd/system/engawa.servicePaste this in and save:
[Unit]Description=engawa daemonAfter=network-online.target postgresql.service redis-server.service
[Service]Type=simpleUser=engawaGroup=engawaUMask=0027WorkingDirectory=/home/engawa/engawaExecStart=/usr/bin/node /home/engawa/engawa/packages/backend/built/boot/entry.jsEnvironment="NODE_ENV=production"TimeoutSec=60StandardOutput=journalStandardError=journalSyslogIdentifier=engawaRestart=always
[Install]WantedBy=multi-user.targetLoad and start it:
sudo systemctl daemon-reloadsudo systemctl enable --now engawaCheck the status:
sudo systemctl status engawaactive (running) is what you want. To follow the logs:
sudo journalctl -u engawa -fLeave with Ctrl+C.
11. Put a reverse proxy in front and serve HTTPS
Section titled “11. Put a reverse proxy in front and serve HTTPS”engawa is running on localhost:3000, which nothing outside the server can reach. Put nginx in front of it to handle HTTPS and certificates.
The repository ships production-ready proxy configuration in contrib/, and that is what this guide uses. It is worth reading contrib/README.md once as well.
Install nginx and the ACME module
Section titled “Install nginx and the ACME module”The ACME module needs nginx 1.29.0 or newer, which Ubuntu’s own repository does not carry. Add the official nginx repository.
sudo apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyringcurl -fsSL https://nginx.org/keys/nginx_signing.key | gpg --dearmor | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg > /dev/nullecho "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/mainline/ubuntu $(lsb_release -cs) nginx" | sudo tee /etc/apt/sources.list.d/nginx.listsudo apt updatesudo apt install -y nginx nginx-module-acmeCheck the version:
nginx -vnginx/1.29.0 or newer is what you need.
Load the ACME module
Section titled “Load the ACME module”Open the main configuration file:
sudo nano /etc/nginx/nginx.confNear the top, outside the http { block, add this one line:
load_module modules/ngx_http_acme_module.so;Add the site configuration
Section titled “Add the site configuration”Copy contrib/nginx.sample.conf from the repository into nginx’s conf.d:
sudo cp /home/engawa/engawa/contrib/nginx.sample.conf /etc/nginx/conf.d/engawa.confReplace the domain and the contact address with your own:
sudo sed -i 's/engawa\.example\.com/example.com/g; s/admin@example\.com/you@example.com/g' /etc/nginx/conf.d/engawa.confOpen the file and confirm the substitution landed:
sudo nano /etc/nginx/conf.d/engawa.confWhile you are there, check that these match your environment:
| Setting | Default | What it is |
|---|---|---|
resolver |
1.1.1.1 1.0.0.1 |
The DNS servers the ACME module uses. Point it at your own resolver if you have one |
ssl_trusted_certificate |
/etc/ssl/certs/ca-certificates.crt |
The CA bundle path on Ubuntu and Debian. Leave it as is |
client_max_body_size |
256m |
The upload limit. Keep it in line with engawa’s maxFileSize |
The official nginx package ships a default site that also listens on port 80. Remove it so it does not compete:
sudo rm -f /etc/nginx/conf.d/default.confCreate the ACME state directory
Section titled “Create the ACME state directory”The certificate and the account key are stored here, and the nginx worker user has to be able to read and write it. With the official package that user is nginx.
sudo install -d -m 700 -o nginx -g nginx /var/lib/nginx/acme-engawaOpen the firewall
Section titled “Open the firewall”sudo ufw allow 80/tcpsudo ufw allow 443/tcpPort 80 is not just for the redirect to HTTPS — the HTTP-01 challenge uses it for the initial issuance and every renewal. Leave it open.
Start it
Section titled “Start it”Check the syntax, then start nginx:
sudo nginx -tsudo systemctl enable --now nginxOnly start once you see syntax is ok and test is successful. If nginx is already running, use sudo systemctl reload nginx.
The first issuance takes a few tens of seconds, and HTTPS does not work until it completes. Watch the progress and any errors in the nginx log:
sudo tail -f /var/log/nginx/error.logVerify
Section titled “Verify”curl -I http://example.com/HTTP/1.1 308 Permanent Redirect means the redirect from HTTP works.
curl -I https://example.com/HTTP/2 200 means the certificate was issued and requests reach engawa.
Open https://example.com in a browser. If the initial setup screen appears, you are done — the first account you create becomes the administrator.
Alternative to step 11: serving HTTPS with Caddy
Section titled “Alternative to step 11: serving HTTPS with Caddy”This is the Caddy route instead of nginx. If you completed step 11, skip this section. Both want ports 80 and 443, so you cannot run them side by side.
Caddy obtains and renews certificates and redirects HTTP to HTTPS out of the box, so there is no extra module to install.
Install Caddy
Section titled “Install Caddy”Ubuntu’s own repository carries Caddy, but it can be outdated, so add the official repository.
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curlcurl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpgcurl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.listsudo apt updatesudo apt install -y caddyCheck the version:
caddy versionSomething starting with v2. means you are good.
Open the firewall
Section titled “Open the firewall”sudo ufw allow 80/tcpsudo ufw allow 443/tcpsudo ufw allow 443/udpPort 80 is used for issuance and renewal (the HTTP-01 challenge), so leave it open. UDP 443 is for HTTP/3; skip it if you do not want HTTP/3.
Add the configuration file
Section titled “Add the configuration file”Copy contrib/Caddyfile from the repository:
sudo cp /home/engawa/engawa/contrib/Caddyfile /etc/caddy/CaddyfileReplace the domain and the contact address with your own. The contact address is where Let’s Encrypt sends certificate notices.
sudo sed -i 's/engawa\.example\.com/example.com/g; s/admin@example\.com/you@example.com/g' /etc/caddy/CaddyfileOpen it and confirm the substitutions landed:
sudo nano /etc/caddy/Caddyfilemax_size under request_body (268435456, i.e. 256 MiB) is meant to line up with engawa’s maxFileSize. If you change one, change the other.
Validate the configuration
Section titled “Validate the configuration”Caddy can format and validate the file. Running both before you start it saves time.
caddy fmt --diff /etc/caddy/CaddyfileA diff here means the formatting is off — it does not mean the config is broken.
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfileValid configuration is what you want.
Start it
Section titled “Start it”sudo systemctl enable --now caddyIf Caddy is already running, use sudo systemctl reload caddy.
The first issuance takes a few tens of seconds. Watch the progress and any errors in the log:
sudo journalctl -u caddy -fVerify
Section titled “Verify”curl -I http://example.com/HTTP/1.1 308 Permanent Redirect means the redirect from HTTP works — Caddy sets that up for you.
curl -I https://example.com/HTTP/2 200 means the certificate was issued and requests reach engawa.
Open https://example.com in a browser. If the initial setup screen appears, you are done — the first account you create becomes the administrator.
Updating
Section titled “Updating”When a new version is released, run this as the engawa user:
sudo systemctl stop engawasudo -iu engawacd engawagit pullpnpm install --frozen-lockfileNODE_ENV=production pnpm buildpnpm migrateexitsudo systemctl start engawaIf the release notes describe extra steps, follow those instead.
When something goes wrong
Section titled “When something goes wrong”| Symptom | What to check |
|---|---|
| The service keeps dying | Read the error with sudo journalctl -u engawa -n 50. A typo in the config file is the usual cause |
| 502 Bad Gateway | Check that engawa is running (systemctl status engawa) and that port matches nginx’s proxy_pass |
| HTTPS does not work / no certificate is issued | Check that port 80 is reachable from outside and that DNS points at this server. Details land in /var/log/nginx/error.log |
nginx will not start: module ... is already loaded |
The load_module line is duplicated between nginx.conf and modules-enabled/. Keep only one |
| Every request appears to come from the same IP | trustProxy is missing from the configuration file |
| The timeline does not update by itself | Check that the nginx WebSocket headers (Upgrade and Connection) are present |
| Cannot connect to the database | Try connecting by hand with psql -h localhost -U engawa -d engawa and confirm the password matches the config file |
| The build stops half way | Out of memory. Add swap as described in step 9 |
| Something is off and you are running Caddy | Read the log with sudo journalctl -u caddy -n 50 |
| No video thumbnails | Check that ffmpeg is installed with ffprobe -version. Restart engawa after installing it |
Whenever you edit the configuration file, restart with sudo systemctl restart engawa — a running process will not pick up the change.