Working Auto Updater

pantha0073 days ago

This script is HIGHLY EXPERIMENTAL.
I have only done very limited testing.
It may contain bugs that I ( and ChatGPT ) may have not caught.
Running it without understanding what it does could completely break your Traccar installation, delete your config.

Hi all I have been working on an auto update script for Traccar running on my Raspberry Pi 5. I have turned it into a step by step tutorial if anyone wants it :-). It will backup your existing config / check your current version / download the latest release / install the new release ( if found) / restore backup and restart the server :-)

  1. Install jq (required to parse the GitHub API responses)
sudo apt-get install jq
  1. Create the main update file
nano ~/traccar-auto-update.sh

Paste in the following :

#!/usr/bin/env bash

set -Eeuo pipefail

DRY_RUN=0
if [[ "${1:-}" == "--dry-run" ]]; then
    DRY_RUN=1
fi

# --- re-exec as root if needed ---
if [[ $EUID -ne 0 && $DRY_RUN -eq 0 ]]; then
  exec sudo -E bash "$0" "$@"
fi

# --- check dependencies ---
for cmd in jq curl unzip systemctl tar; do
    command -v "$cmd" >/dev/null 2>&1 || { echo "$cmd is required. Please install it first."; exit 1; }
done

# --- paths & settings ---
TRACCAR_DIR="/opt/traccar"
TRACCAR_USER="traccar"
TRACCAR_CONF_FILE="$TRACCAR_DIR/conf/traccar.xml"
TMP_DIR="/tmp/traccar-update"
BACKUP_DIR="/opt/traccar-backup"
STATE_FILE="/home/craig/traccar-current-version.txt"
LOG_TAG="Traccar-Update"

log(){ echo "$(date '+%F %T') $LOG_TAG: $*"; }

trim_ver(){ printf "%s" "$1" | tr -d '\r' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/^v//' ; }

get_installed_version(){
  local v=""
  if [[ -f "$TRACCAR_CONF_FILE" ]]; then
    v=$(grep -oP '<entry key="version.build">\K(.*?)(?=</entry>)' "$TRACCAR_CONF_FILE" || true)
  fi
  if [[ -z "${v:-}" && -f "$STATE_FILE" ]]; then
    v=$(head -n1 "$STATE_FILE" || true)
  fi
  if [[ -z "${v:-}" && -f "$TRACCAR_DIR/tracker-server.jar" ]]; then
    v=$(unzip -p "$TRACCAR_DIR/tracker-server.jar" META-INF/MANIFEST.MF 2>/dev/null \
         | awk -F': ' '/^Implementation-Version:/ {print $2; exit}' || true)
  fi
  trim_ver "${v:-}"
}

get_latest_version(){
  local tag=""
  tag=$(curl -fsSL https://api.github.com/repos/traccar/traccar/releases/latest | jq -r '.tag_name' || true)
  trim_ver "${tag:-}"
}

# --- start ---
mkdir -p "$TMP_DIR" "$BACKUP_DIR"

INSTALLED_VERSION="$(get_installed_version)"
LATEST_VERSION="$(get_latest_version)"

if [[ -z "$LATEST_VERSION" ]]; then
  log "ERROR: Could not determine latest version from GitHub. Aborting."
  exit 1
fi

log "Installed version: ${INSTALLED_VERSION:-unknown}"
log "Latest version: $LATEST_VERSION"

if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then
  log "Installed version ($INSTALLED_VERSION) matches latest ($LATEST_VERSION). No update required."
  exit 0
fi

log "Update available: $LATEST_VERSION"

if [[ $DRY_RUN -eq 1 ]]; then
    log "DRY-RUN: Would back up config and installation to $BACKUP_DIR"
    log "DRY-RUN: Would download installer for version $LATEST_VERSION"
    log "DRY-RUN: Would stop traccar, install new version, restore config, adjust permissions, and start service"
    exit 0
fi

# --- backup config and installation ---
TS="$(date +%F_%H-%M-%S)"
if [[ -f "$TRACCAR_CONF_FILE" ]]; then
  CONF_BAK="$BACKUP_DIR/traccar.xml.$TS"
  cp -a "$TRACCAR_CONF_FILE" "$CONF_BAK"
  log "Backed up config to $CONF_BAK"
else
  log "WARNING: $TRACCAR_CONF_FILE not found; continuing."
fi

if [[ -d "$TRACCAR_DIR" ]]; then
  TAR_BAK="$BACKUP_DIR/traccar-${INSTALLED_VERSION:-unknown}-$TS.tgz"
  tar -C /opt -czf "$TAR_BAK" traccar || { log "WARNING: backup archive failed"; }
  log "Backed up install to $TAR_BAK"
fi

# --- determine architecture ---
ARCH=$(uname -m)
case "$ARCH" in
  x86_64) ZIP_ARCH="x64" ;;
  aarch64|arm64) ZIP_ARCH="arm64" ;;
  armv7*) ZIP_ARCH="arm" ;;
  *) log "Unsupported architecture: $ARCH"; exit 1 ;;
esac

# --- fetch release zip ---
VNUM="$LATEST_VERSION"
ZIP_URL="https://github.com/traccar/traccar/releases/download/v$VNUM/traccar-linux-$ZIP_ARCH-$VNUM.zip"
ZIP_FILE="$TMP_DIR/traccar-$VNUM.zip"

log "Downloading $ZIP_URL ..."
curl -fL "$ZIP_URL" -o "$ZIP_FILE"
log "Download saved to $ZIP_FILE"

# --- extract traccar.run and install ---
rm -rf "$TMP_DIR/new"
mkdir -p "$TMP_DIR/new"
unzip -oq "$ZIP_FILE" -d "$TMP_DIR/new"

if [[ ! -f "$TMP_DIR/new/traccar.run" ]]; then
  log "ERROR: traccar.run not found in ZIP. Aborting."
  exit 1
fi

chmod +x "$TMP_DIR/new/traccar.run"

log "Stopping Traccar..."
systemctl stop traccar || log "Warning: failed to stop traccar service"

log "Installing to $TRACCAR_DIR ..."
"$TMP_DIR/new/traccar.run" --accept --target "$TRACCAR_DIR"

if [[ -f "${CONF_BAK:-/nonexistent}" ]]; then
  cp -a "$CONF_BAK" "$TRACCAR_CONF_FILE"
  log "Restored config from $CONF_BAK"
fi

# --- permissions ---
chown -R "$TRACCAR_USER":"$TRACCAR_USER" "$TRACCAR_DIR"
chmod -R 755 "$TRACCAR_DIR"
[[ -f "$TRACCAR_CONF_FILE" ]] && chmod 644 "$TRACCAR_CONF_FILE"

log "Starting Traccar..."
systemctl daemon-reload
if ! systemctl start traccar; then
  log "ERROR: Failed to start traccar service"
  exit 1
fi

# --- cleanup ---
rm -rf "$TMP_DIR/new" "$ZIP_FILE"

# --- write state file ---
printf "%s\n" "$VNUM" > "$STATE_FILE"

log "Update completed to $VNUM."

THEN SAVE AND EXIT

  1. Make the file executable
chmod +x ~/traccar-auto-update.sh
  1. Then you can run it with:
sudo ~/traccar-auto-update.sh
  1. Create a systemd service file NOTE: ExecStart= Change Path As Needed
sudo nano /etc/systemd/system/traccar-auto-update.service

Paste in the following :

[Unit]
Description=Traccar Auto Update Service
After=network.target

[Service]
Type=oneshot
ExecStart=/home/craig/traccar-auto-update.sh
StandardOutput=append:/var/log/traccar-auto-update.log
StandardError=append:/var/log/traccar-auto-update.log

[Install]
WantedBy=multi-user.target

THEN SAVE AND EXIT

  1. Create a systemd timer Currently set to check for updates at 03:00
sudo nano /etc/systemd/system/traccar-auto-update.timer

Paste in the following :

[Unit]
Description=Daily Traccar Auto Update Timer

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
Unit=traccar-auto-update.service

[Install]
WantedBy=timers.target

THEN SAVE AND EXIT

  1. Enable and start the timer

Reload systemd so it sees any edits

sudo systemctl daemon-reload  

Enable + start the timer

sudo systemctl enable --now traccar-auto-update.timer  

Check if it’s running

systemctl list-timers --all | grep traccar-auto-update
  1. Optional: Test manually
sudo systemctl start traccar-auto-update.service
tail -n 50 /home/craig/traccar-auto-update.log

Change /home/craig to your own location

Anton Tananaev3 days ago

Are you sure you wrote this script? Or ChatGPT did? And have you tested it?

pantha0073 days ago

I could never have created the main script without GPT's help ( it took a week of arguing with it to get it to this stage ) and yes it's running on my Pi :-)

Anton Tananaev3 days ago

Well, unfortunately it's incorrect. The version check won't work. It tries to read a version from the config file, which doesn't exist, unless you manually define it.

pantha0073 days ago

That was the old script damm , thanks for pointing out this issue please see below the fixed one which also has a Dry Run mode added ( if this works ok i need to find out how to edit my original post lol ) please dont be too rough on me this is the first thing I have put together (with a lot of help from chat GPT)

#!/usr/bin/env bash

set -Eeuo pipefail

DRY_RUN=0
if [[ "${1:-}" == "--dry-run" ]]; then
    DRY_RUN=1
fi

# --- re-exec as root if needed ---
if [[ $EUID -ne 0 && $DRY_RUN -eq 0 ]]; then
  exec sudo -E bash "$0" "$@"
fi

# --- check dependencies ---
for cmd in jq curl unzip systemctl tar; do
    command -v "$cmd" >/dev/null 2>&1 || { echo "$cmd is required. Please install it first."; exit 1; }
done

# --- paths & settings ---
TRACCAR_DIR="/opt/traccar"
TRACCAR_USER="traccar"
TRACCAR_CONF_FILE="$TRACCAR_DIR/conf/traccar.xml"
TMP_DIR="/tmp/traccar-update"
BACKUP_DIR="/opt/traccar-backup"
STATE_FILE="/home/craig/traccar-current-version.txt"
LOG_TAG="Traccar-Update"

log(){ echo "$(date '+%F %T') $LOG_TAG: $*"; }

trim_ver(){ printf "%s" "$1" | tr -d '\r' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/^v//' ; }

get_installed_version(){
  local v=""
  if [[ -f "$TRACCAR_CONF_FILE" ]]; then
    v=$(grep -oP '<entry key="version.build">\K(.*?)(?=</entry>)' "$TRACCAR_CONF_FILE" || true)
  fi
  if [[ -z "${v:-}" && -f "$STATE_FILE" ]]; then
    v=$(head -n1 "$STATE_FILE" || true)
  fi
  if [[ -z "${v:-}" && -f "$TRACCAR_DIR/tracker-server.jar" ]]; then
    v=$(unzip -p "$TRACCAR_DIR/tracker-server.jar" META-INF/MANIFEST.MF 2>/dev/null \
         | awk -F': ' '/^Implementation-Version:/ {print $2; exit}' || true)
  fi
  trim_ver "${v:-}"
}

get_latest_version(){
  local tag=""
  tag=$(curl -fsSL https://api.github.com/repos/traccar/traccar/releases/latest | jq -r '.tag_name' || true)
  trim_ver "${tag:-}"
}

# --- start ---
mkdir -p "$TMP_DIR" "$BACKUP_DIR"

INSTALLED_VERSION="$(get_installed_version)"
LATEST_VERSION="$(get_latest_version)"

if [[ -z "$LATEST_VERSION" ]]; then
  log "ERROR: Could not determine latest version from GitHub. Aborting."
  exit 1
fi

log "Installed version: ${INSTALLED_VERSION:-unknown}"
log "Latest version: $LATEST_VERSION"

if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then
  log "Installed version ($INSTALLED_VERSION) matches latest ($LATEST_VERSION). No update required."
  exit 0
fi

log "Update available: $LATEST_VERSION"

if [[ $DRY_RUN -eq 1 ]]; then
    log "DRY-RUN: Would back up config and installation to $BACKUP_DIR"
    log "DRY-RUN: Would download installer for version $LATEST_VERSION"
    log "DRY-RUN: Would stop traccar, install new version, restore config, adjust permissions, and start service"
    exit 0
fi

# --- backup config and installation ---
TS="$(date +%F_%H-%M-%S)"
if [[ -f "$TRACCAR_CONF_FILE" ]]; then
  CONF_BAK="$BACKUP_DIR/traccar.xml.$TS"
  cp -a "$TRACCAR_CONF_FILE" "$CONF_BAK"
  log "Backed up config to $CONF_BAK"
else
  log "WARNING: $TRACCAR_CONF_FILE not found; continuing."
fi

if [[ -d "$TRACCAR_DIR" ]]; then
  TAR_BAK="$BACKUP_DIR/traccar-${INSTALLED_VERSION:-unknown}-$TS.tgz"
  tar -C /opt -czf "$TAR_BAK" traccar || { log "WARNING: backup archive failed"; }
  log "Backed up install to $TAR_BAK"
fi

# --- determine architecture ---
ARCH=$(uname -m)
case "$ARCH" in
  x86_64) ZIP_ARCH="x64" ;;
  aarch64|arm64) ZIP_ARCH="arm64" ;;
  armv7*) ZIP_ARCH="arm" ;;
  *) log "Unsupported architecture: $ARCH"; exit 1 ;;
esac

# --- fetch release zip ---
VNUM="$LATEST_VERSION"
ZIP_URL="https://github.com/traccar/traccar/releases/download/v$VNUM/traccar-linux-$ZIP_ARCH-$VNUM.zip"
ZIP_FILE="$TMP_DIR/traccar-$VNUM.zip"

log "Downloading $ZIP_URL ..."
curl -fL "$ZIP_URL" -o "$ZIP_FILE"
log "Download saved to $ZIP_FILE"

# --- extract traccar.run and install ---
rm -rf "$TMP_DIR/new"
mkdir -p "$TMP_DIR/new"
unzip -oq "$ZIP_FILE" -d "$TMP_DIR/new"

if [[ ! -f "$TMP_DIR/new/traccar.run" ]]; then
  log "ERROR: traccar.run not found in ZIP. Aborting."
  exit 1
fi

chmod +x "$TMP_DIR/new/traccar.run"

log "Stopping Traccar..."
systemctl stop traccar || log "Warning: failed to stop traccar service"

log "Installing to $TRACCAR_DIR ..."
"$TMP_DIR/new/traccar.run" --accept --target "$TRACCAR_DIR"

if [[ -f "${CONF_BAK:-/nonexistent}" ]]; then
  cp -a "$CONF_BAK" "$TRACCAR_CONF_FILE"
  log "Restored config from $CONF_BAK"
fi

# --- permissions ---
chown -R "$TRACCAR_USER":"$TRACCAR_USER" "$TRACCAR_DIR"
chmod -R 755 "$TRACCAR_DIR"
[[ -f "$TRACCAR_CONF_FILE" ]] && chmod 644 "$TRACCAR_CONF_FILE"

log "Starting Traccar..."
systemctl daemon-reload
if ! systemctl start traccar; then
  log "ERROR: Failed to start traccar service"
  exit 1
fi

# --- cleanup ---
rm -rf "$TMP_DIR/new" "$ZIP_FILE"

# --- write state file ---
printf "%s\n" "$VNUM" > "$STATE_FILE"

log "Update completed to $VNUM."
Anton Tananaev3 days ago

Can you share ChatGPT chat for this generation?

pantha0073 days ago

this was the first main chat "https://chatgpt.com/share/68adc955-9908-8006-9cb3-7bc55c853633" and this was the second part where i basically asked GPT to check its own work and fix it "https://chatgpt.com/share/68adc9e6-f580-800d-88c2-8fae8151ae91" I gotta warn you the first one was a very long conversation lol. I just hope this works ok for everyone at some point :-)

Anton Tananaev3 days ago

It looks like the script was fixed just now. Or am I misreading this? So the original scrip wasn't "old".

pantha0073 days ago

you are correct, the original script did run on mine ( dont know why ) so i asked GPT for help today

Anton Tananaev3 days ago

I appreciate sharing it, but I think you should provide very clear warnings about the code. It seems like you did very limited testing. It seems like you might have fixed the issue I pointed out, but I'm worried there are more errors. And with something as important as updating, someone can completely mess up their setup very quickly.

Anton Tananaev3 days ago

If you really want to automate updates, I would probably recommend using Docker.

pantha0073 days ago

I shall delete this post and make a new one with everything up to date and add warnings and note this is still being tested. Thankyou for looking at it :-) and pointing out the issues :-).

Craig

Anton Tananaev3 days ago

It's fine to keep this. I can update the original post for you if needed.

pantha0073 days ago

amazing thankyou if you could add the fixed code and also add some warnings please, something like

This script is HIGHLY EXPERIMENTAL.
I have only done very limited testing.
It may contain bugs that I ( and ChatGPT ) may have not caught.
Running it without understanding what it does could completely break your Traccar installation, delete your config.

If you decide to try this:

Use --dry-run first. That will show what it would do without making changes.

Many thanks again for your help :-)

Anton Tananaev3 days ago

Updated the script and description.