A simple Bash script for installing and updating Element Web from GitHub releases.
Warning
Back up your config.json before using this script.
The script is designed to preserve config.json during updates, but you should always keep a separate backup copy in a safe location. Use this script at your own risk.
- Automatically detects the latest Element Web release.
- Checks the currently installed version and skips the update if it is already up to date.
- Supports both fresh installations and updates.
- Preserves the existing
config.json. - Downloads and verifies the release archive (SHA-256) before touching the current installation.
- Removes files from previous releases that are no longer present in the new release.
- Shows a download progress bar when run interactively and stays quiet under
systemd, cron, or another non-interactive environment.
- Bash
curlsha256sumtarfindsedandawk(POSIX, normally preinstalled)
No additional packages or runtime environments are required.
The script is configured using variables at the very top of the script. All of them can also be overridden using an optional .env file.
The directory where Element Web is installed.
There is no default: DESTINATION must always be provided, either in the .env file or at the top of the script.
Example:
DESTINATION="/var/www/element"The directory used for downloading the release archive and temporarily extracting it before installation.
It must be different from DESTINATION.
Default:
TMP_DIR="/tmp"Example:
TMP_DIR="/var/tmp/element"The GitHub repository from which the Element Web releases are downloaded.
Default:
REPO="element-hq/element-web"Example:
REPO="element-hq/element-web"The .env file is optional. If present in the current working directory, it overrides the default values.
Only the DESTINATION, TMP_DIR and REPO keys are read; everything else is ignored. Values may be quoted, comments and empty lines are allowed. The file is parsed, not executed.
Example:
DESTINATION="/var/www/element"
TMP_DIR="/var/tmp/element"
REPO="element-hq/element-web"If no .env file is provided, the default values defined in the script are used.
Because the file is looked up in the current working directory, when running the script from cron or a systemd timer make sure the working directory (the WorkingDirectory= option in systemd, or a cd in cron) points to the directory that contains .env.
The installed version is read from $DESTINATION/version. This file is shipped inside the release archive itself (its value includes a v prefix, e.g. v1.12.25, which the script normalizes), so the script does not maintain its own version file.
For a fresh install, $DESTINATION must be empty except for config.json. This prevents the script from accidentally overwriting unrelated files.
For an existing installation, the script:
- Checks the latest Element Web release on GitHub.
- Downloads the release archive to
$TMP_DIRand verifies its SHA-256 digest. - Extracts the archive to a temporary directory.
- Verifies the archive looks like Element Web (contains
index.htmlandversion). - Removes the old installation files (keeping
config.json) and copies the new release into place. - The
versionfile is copied together with the other files, so the installed version is always tracked.
If downloading, verifying or extracting fails, the current installation is left untouched.
Note: step 5 is not atomic — the old files are deleted before the new ones are copied. A failure during the copy (for example a full disk) can leave a partial installation. Keep a backup.
Make the script executable and run it:
chmod +x element-web-updater.sh
./element-web-updater.shor run it directly with Bash:
bash element-web-updater.shFiles are copied with the permissions and ownership of the user running the script. If your web server runs under a different user (for example www-data), make sure the installation directory is writable by the script's user, or run the script as the web server user and adjust ownership afterwards.
The script can also be executed periodically using systemd timers. See Scheduled updates (systemd timer).
Release archives are published on the Releases page and contain:
element-web-updater-<version>.tar.gz— the script, theREADME.mdand theLICENSE(MIT);SHA256SUMS— checksums of the archive for integrity verification.
Verify the archive before using it:
sha256sum -c SHA256SUMSThe project is licensed under the MIT license.
The updater can be run on a schedule with a systemd timer. The service should run as the user that runs the web server (for example www-data), so that the installed files get the correct ownership and permissions.
Create /etc/systemd/system/element-web-updater.service:
[Unit]
Description=Update Element Web
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=www-data
Group=www-data
WorkingDirectory=/srv/element-web-updater
ExecStart=/srv/element-web-updater/element-web-updater.sh
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
[Install]
WantedBy=multi-user.targetNotes on the service unit:
User=andGroup=must be set to the user that runs the web server (herewww-data). That user must be able to read and execute the updater script and to write toDESTINATION.WorkingDirectory=must point to the directory that contains the updater's.envfile — the script looks it up in the current working directory. If you do not use a.envfile, point it to any directory and configureDESTINATION,TMP_DIRandREPOat the top of the script instead.PrivateTmp=truegives the service an isolated temporary directory. With the defaultTMP_DIR=/tmpthe download and extraction happen inside that private directory and are cleaned up automatically.- When run by systemd, stdout is not a terminal, so the script stays quiet and does not show a progress bar.
Create /etc/systemd/system/element-web-updater.timer:
[Unit]
Description=Run the Element Web updater daily
[Timer]
OnCalendar=daily
RandomizedDelaySec=15min
Persistent=true
[Install]
WantedBy=timers.targetOnCalendar=dailyruns the update once a day; other schedules are possible, for exampleOnCalendar=*-*-* 03:00:00for a fixed time.RandomizedDelaySecspreads the run over up to 15 minutes to avoid load spikes.Persistent=trueruns a missed update right after boot if the machine was off at the scheduled time.
Make sure the installation directory is writable by the web server user, and that the updater script and its .env file are readable by it:
sudo chown -R www-data:www-data /var/www/elementThen enable and start the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now element-web-updater.timerCheck the schedule and the results of the last run:
systemctl list-timers element-web-updater.timer
systemctl status element-web-updater.service
journalctl -u element-web-updater.serviceTo trigger an update manually under the same conditions as the timer:
sudo systemctl start element-web-updater.service- If
DESTINATIONis not writable by the user set withUser=, the update will fail. Either run the service as the owner of the installation directory, or change its ownership withchown. - With
ProtectSystem=full, the directories/usr,/bootand/etcare read-only.DESTINATIONandTMP_DIRmust live outside of them; if that does not fit your setup, drop or adjust the hardening options. - The examples assume the web server runs under
www-dataand that.envcontainsDESTINATION="/var/www/element". AdjustUser=,Group=,WorkingDirectory=and thechowntarget to your environment.
The config.json file is considered user-managed and is never overwritten by the script.
The release archive ships a config.sample.json instead of a config.json. As a safety measure the script also removes any config.json from the extracted archive before copying, so a preserved user configuration always survives an update.
The script preserves the existing config.json during installation and updates. However, you should always keep a backup copy of your config.json in a safe location before using the script.
The author is not responsible for any data loss, configuration loss, service interruption, or other damage resulting from the use of this script.
Use the script at your own risk and make sure you have a working backup before performing an update.