Skip to main content

Installation

GEM can be installed on any Debian-based Linux system (Ubuntu, Debian, etc.) using either the remote installer or a local development setup. This includes Windows systems running WSL (Windows Subsystem for Linux). The installer handles system dependencies, database configuration, notifications, and service creation through a web-based installer.

System Requirements

  • OS: Ubuntu 20.04+ or compatible Debian-based Linux distribution (including WSL on Windows)
  • RAM: 2 GB minimum
  • Disk: 10 GB free space
  • Network: Internet connectivity (for package installation)
  • Privileges: Root / sudo access

Quick Install

Install GEM with a single command:

wget -qO- https://updates.mygem.us/install.sh | sh

This runs as your normal user and elevates with sudo when needed (you may be prompted for your password), then downloads the latest self-extracting installer (.run file), verifies its checksum, and runs it. The installer:

  1. Prompts for an installation directory (default: /opt/gem) — only when run from an interactive terminal
  2. Installs system dependencies (build tools, PostgreSQL, Node.js 22, FFmpeg, etc.)
  3. Installs npm packages
  4. Launches the installer in a browser

Unattended Installation

The shell installer never blocks on a keystroke when there is no interactive terminal (for example an automated provisioning run with no controlling TTY). In that case it uses the defaults, or the values supplied through environment variables:

VariableEffect
GEM_INSTALL_DIRInstallation directory. Skips the directory prompt entirely. Defaults to /opt/gem.
GEM_FORCE=1Overwrite an existing, non-empty installation directory without confirmation. An existing gem.json is still backed up first.
GEM_INSTALL_DIR=/srv/gem GEM_FORCE=1 wget -qO- https://updates.mygem.us/install.sh | sh

If the target directory already exists and is not empty, a non-interactive run without GEM_FORCE=1 aborts rather than overwriting — set GEM_INSTALL_DIR to a fresh path or pass GEM_FORCE=1 to proceed. Interactive runs still prompt for the directory and for overwrite confirmation as before.

Installation Methods

Remote Installer (Production)

The recommended method for production deployments. The install.sh script served by the update server:

  1. Downloads the latest versioned .run installer
  2. Verifies the MD5 checksum
  3. Makes the installer executable and runs it

The .run file is a self-extracting archive (built with makeself) containing the pre-built GEM application and a setup.sh script.

Installer

After dependencies are installed, the installer launches as a local web server. On a desktop session it tries to open in your browser automatically — this is best-effort, and because the installer runs under sudo it opens the page in the invoking desktop user's session rather than root's. It looks for a real browser in order — Chrome, then Chromium, then Firefox — and launches the first one found (rather than the system default handler, which on some desktops is mis-bound to a non-browser app such as a text editor). If none of those browsers is installed, or the page does not open, type the http:// address printed in the terminal into any browser; the printed URL is always the reliable way to reach the wizard. It walks through six steps:

Step 1: Welcome & Preflight

Displays system requirements and runs automatic preflight checks:

CheckRequirement
Disk space10 GB free
Memory2 GB RAM
Sudo accessRunning as root
PostgreSQLInstalled (or will be)
NetworkInternet connectivity
Architecturex86_64 or ARM
GEM serviceDetects existing installation

Node.js v22 is bundled with the installer, so a system Node install is not required.

If an existing gem.json is found, the installer loads the current configuration and creates a timestamped backup (gem.json.bak.YYYY-MM-DDTHHMMSS).

Step 2: Dependencies

Select which components to install. Each dependency is installed individually with progress tracking:

ComponentDescriptionDefault
PostgreSQLDatabase serverChecked
Node.jsJavaScript runtime (v22)Checked
System Dependenciesbuild-essential, git, mDNS, nmap, arp-scan, FFmpeg, adbChecked
Python & ModulesPython 3, pyatv (Apple TV), lennoxs30api, netaudioUnchecked
Matter (chip-tool)BlueZ, Avahi, and chip-tool for Matter device commissioningUnchecked

Dependencies use two installation mechanisms:

  • [apt] packages are installed via apt install -y with a 5-minute timeout per package
  • [cmd] commands are executed via bash with a 10-minute timeout

Step 3: Database

Configure the PostgreSQL connection. For fresh PostgreSQL installations, the installer can create the database user and database automatically.

Fields:

FieldDefaultDescription
HostlocalhostDatabase server address
Port5432PostgreSQL port
Username(empty)Database user (created if "Create Database" is checked)
Password(empty)Database password (use "Generate" for a random password)
Database Namegem_dbDatabase name

Options:

  • Create Database -- Creates the PostgreSQL user, database, gem schema, all tables, and transfers ownership. Also configures pg_hba.conf for TCP password authentication if needed. The installer auto-unchecks and disables this option only when both the database and its login role already exist. If the database exists but the configured user is missing -- a half-created state left by an interrupted prior run -- the installer keeps Create Database checked and enabled and shows a warning, so the next run can add the missing user instead of shipping a database with no usable login.
  • Seed Database -- Populates the database with the default theme data (create_theme.sql).
  • Test Connection -- Verifies the database is ready. The check adapts to the Create Database option:
    • Create Database checked (fresh install) -- the GEM user and database don't exist yet, so authenticating as them would always fail with "password authentication failed." Instead the test confirms PostgreSQL is reachable as the postgres superuser (the account used to create the role and database) and reports what Continue will do — either that the user and database will be created, or, if a previous run already created them and the credentials work, that they will be reused.
    • Create Database unchecked -- authenticates as the supplied user against the existing database, exactly as before.
Database step is a hard gate

The Database step will not advance until GEM can actually authenticate as the configured user against the configured database. After running setup, the installer performs a final connection check as that user; if it fails, the installer surfaces an actionable error and stays on Step 3 rather than continuing. This prevents an install from "completing successfully" with a database it can never reach. If verification fails, enable Create Database to create the missing user/database, or correct the credentials, then try again.

GEM Admin Account:

FieldDefaultDescription
Admin UsernameadminThe login you will use to access the GEM web interface. Recommended to change from the default.
Admin Password(empty)Leave blank to auto-generate a strong password — it is shown on the final screen and nowhere else.

If you set the password manually, it must be at least 12 characters and mix at least three of: lowercase, uppercase, digit, symbol. Click Generate for a strong default that satisfies the rule automatically.

The password you provide here is handed to the GEM server on first boot, used to seed the admin user, and then scrubbed from gem.json (the key is only ever encrypted at rest in the database after that).

Step 4: Notifications (Optional)

Configure email and SMS for system alerts. This step can be skipped and configured later from the admin panel.

Email (SMTP):

  • Host, port, username, password
  • From/to addresses
  • Test button sends a verification email

SMS (Telnyx):

  • From/to phone numbers
  • Telnyx API key
  • Test button sends a verification SMS

Step 5: Server Configuration

SettingDefaultDescription
Location Name(empty)Friendly name for this installation
External URL(empty)Public URL (for remote access / mobile app)
HTTP Port3000Port the GEM web interface listens on when SSL is disabled. Grayed out while SSL is enabled — the app then listens on the HTTPS Port instead — but the value is still saved.
Timezone(auto-detected)Server timezone for schedules and logs
Enable SSLOffRun over HTTPS with auto-generated certificates
HTTPS Port8443Port the GEM web interface listens on when SSL is enabled
HTTP Cert Port8080SSL only: plain-HTTP port that redirects visitors to HTTPS and serves the CA certificate for download
Auto-Regenerate CertsOnRenew certificates on expiry or IP change
Enable REST APIOnAllow external API access
Device AlertsOnSend alerts for device communication failures
Startup AlertOffEmail a notification when GEM starts or restarts
Failed-Start AlertOnEmail a notification if GEM fails to start
Install as ServiceOnCreate a systemd service unit
Start ServiceOnStart GEM immediately after installation

The port field checks whether the chosen port is already in use before proceeding.

Step 6: Complete

Displays an installation summary and provides:

  • The URL to access GEM (e.g., http://localhost:3000/admin/setup)
  • The admin username and password you configured in Step 3 (if the password was auto-generated, it is shown here — copy it now; it is not shown again)
  • Service management command: sudo systemctl status gem

If "Start Service" was selected, the installer starts the systemd service and the Complete step waits for GEM to come up — it polls until GEM's web listener is accepting connections, showing a "Starting GEM…" status. The Open GEM button stays disabled until GEM responds (or a ~90-second timeout elapses), so clicking it never lands on a refused connection. Clicking Open GEM navigates to the new install and shuts the installer down (it exposes privileged, sudo-level endpoints and should not keep running).

Systemd Service

When "Install as Service" is selected, the installer creates /etc/systemd/system/gem.service:

[Unit]
Description=GEM by Harness Automation
After=network-online.target postgresql.service

[Service]
ExecStart=[dir]/node/bin/node [dir]/server.js
WorkingDirectory=[dir]
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=gem
Environment=NODE_ENV=production PORT=[port]

[Install]
WantedBy=multi-user.target

[dir] and [port] are replaced with the actual installation path and configured port.

Service Commands

# Check status
sudo systemctl status gem

# Start / stop / restart
sudo systemctl start gem
sudo systemctl stop gem
sudo systemctl restart gem

# View logs
sudo journalctl -u gem -f

# Enable / disable auto-start
sudo systemctl enable gem
sudo systemctl disable gem

WSL (Windows Subsystem for Linux)

GEM runs on WSL with Ubuntu or Debian. On Windows, the recommended path is the GEM Windows installer (https://updates.mygem.us/windows/GEM-Setup.exe), a setup wizard that provisions WSL 2 with Ubuntu 24.04, enables systemd, configures mirrored networking, and runs the Linux installer inside the distro automatically. The notes below apply when installing GEM into a manually provisioned WSL distro. The installer automatically detects WSL and adapts:

systemd Requirement

The GEM service requires systemd, which is not enabled by default in WSL. If systemd is not active:

  • The installer disables the "Install as Service" and "Start Service" checkboxes
  • You can start GEM manually: ./node/bin/node server.js

To enable systemd in WSL, add to /etc/wsl.conf:

[boot]
systemd=true

Then restart WSL from PowerShell:

wsl --shutdown

After restarting, systemd will be active and the installer can create the GEM service normally.

PostgreSQL on WSL

PostgreSQL installs normally on WSL but may not auto-start. The installer handles this by explicitly starting and enabling PostgreSQL after installation, and checking its status before database operations.

Headless / SSH Installation

When no display server is detected (no DISPLAY or WAYLAND_DISPLAY environment variable), the installer runs in headless mode:

  • The browser is not auto-opened
  • The installer URL is displayed prominently in the terminal with a bordered banner
  • The installer is accessible from any browser on the network

This is useful when installing GEM over SSH on a remote server. Open the displayed URL in a browser on your local machine to complete the installer.

Configuration File

The installer saves all settings to gem.json in the project root. Key sections:

SectionContents
data.connectionPostgreSQL host, port, user, password, database
data.sequelizeSequelize options (dialect, schema, timezone, etc.)
mailSMTP host, port, auth, from/to addresses
smsTelnyx from/to numbers and API key
sslSSL enabled, HTTPS port, HTTP port, auto-regenerate
rest_apiREST API enabled
device_alertsDevice failure alerts enabled
start_alertEmail on GEM start/restart
failed_start_alertEmail when GEM fails to start
external_urlPublic-facing URL
locationInstallation location name
update_urlUpdate server URL

Existing Installation Detection

When the installer detects an existing gem.json:

  1. Creates a timestamped backup of the current config
  2. Loads existing configuration values into the installer fields
  3. Allows reviewing and modifying settings
  4. Supports Save & Exit to save configuration changes without re-running dependency installation

Uninstallation

Remove GEM using the uninstall script:

sudo ./bin/uninstall.sh

This interactively:

  1. Stops and disables the GEM systemd service
  2. Removes the service file and reloads systemd
  3. Optionally drops the PostgreSQL database and user (reads credentials from gem.json)
  4. Optionally removes the GEM application directory

The database is preserved by default unless explicitly confirmed.

Updates

GEM supports over-the-air updates from the update server (updates.mygem.us). The update system:

  1. Check: GEM periodically posts its installation_id to /check_update to see if a new version is available
  2. Download: If an update exists, GEM downloads the encrypted archive (.tar.gz.enc) from /pull_update
  3. Apply: The archive is decrypted and extracted to replace the application files

Updates are encrypted with AES and each version has its own encryption key stored on the update server. Only installations that are registered and enabled on the update server can receive updates.

Installation Registration

Each GEM installation has a unique installation_id (MD5 hash). When a new installation checks for updates:

  1. The update server auto-discovers it and creates a disabled entry
  2. An administrator enables the installation in the update server admin panel
  3. Subsequent update checks succeed and return available versions

Build & Publish Pipeline

For developers building and distributing GEM:

Building an Installer

npm run build:installer

This runs scripts/build-installer.sh which:

  1. Verifies dist/ exists (run npm run build first)
  2. Copies built files to a temp directory
  3. Generates a setup.sh bootstrap script
  4. Packages everything into a self-extracting .run file using makeself
  5. Outputs to install/gem-installer-{version}.run

Publishing an Update

node scripts/publish-update.js
  1. Requires a clean working tree, then runs lint and the unit suite as a release gate — a failure aborts before anything is built, versioned, or uploaded
  2. Runs a full build (npm run build)
  3. Creates an encrypted .tar.gz.enc archive of dist/
  4. Uploads to the update server at /push_update with authentication headers
  5. Pushes the version commit and tag to origin

Publishing an Installer

node scripts/publish-installer.js
  1. Runs build:installer to create the .run file
  2. Uploads to the update server at /push_installer with authentication headers

Both publish scripts require update_url and update_key in gem.json.

Troubleshooting

Installer Won't Start

Symptoms: The installer exits with an error before the setup page opens

Causes:

  • Not running as root -- use sudo
  • Not a Debian-based system -- apt-get is required
  • No internet connectivity -- required for package downloads

Database Creation Fails

Symptoms: Step 3 shows an error after clicking "Create Database"

Common Causes:

ErrorSolution
ECONNREFUSEDPostgreSQL is not running. Start it: sudo systemctl start postgresql
pg_hba.conf rejectionThe installer auto-configures this -- try again. Or manually add scram-sha-256 auth for 127.0.0.1/32
permission deniedEnsure the installer is running with sudo
does not existMake sure "Create Database" is checked
password authentication failedUser exists with a different password. The installer will update it if "Create Database" is checked
Step 3 won't advance / "could not be verified as <user>"The database exists but its login role does not (a half-created state), or the credentials are wrong. Enable "Create Database" to add the user, or correct the credentials, then retry

Connection Lost During Installation

The installer shows a "Connection Lost" modal with automatic reconnection. If the installer process was killed:

  1. Re-run the installer (wget -qO- https://updates.mygem.us/install.sh | sh, or the downloaded .run file)
  2. The installer detects the existing gem.json and loads your previous settings so you can continue

Forgotten Admin Password

If you've lost the admin password and SMTP isn't configured for the email reset flow, run the recovery script with shell access to the host:

sudo -u gem node bin/reset-admin.js # resets the "admin" account
sudo -u gem node bin/reset-admin.js <username> # resets a named user

The script generates a random 16-character password, prints it to the terminal, and sets the must-change-password flag so you'll be prompted to choose a permanent password on first login. Shell ownership of the host is the security boundary — there is no equivalent network-exposed endpoint.

Service Won't Start

# Check service status and logs
sudo systemctl status gem
sudo journalctl -u gem --no-pager -n 50

Common Causes:

  • Database not running -- sudo systemctl start postgresql
  • Port already in use -- change the port in gem.json and the service file
  • Missing npm packages -- run npm install in the GEM directory