On this page
- Check your current version
- Check for updates
- Pre-upgrade checklist
- Back up before upgrading
- Recommended upgrade flow
- Run the upgrade
- Other upgrade methods
- Release channels
- Restart Temps
- Split-process upgrade (zero-downtime)
- Verify the upgrade
- Upgrade the database
- Switch to Enterprise Edition
- Rolling back
- Troubleshooting
Upgrade Temps
Temps includes a self-upgrade command that downloads the latest release from GitHub, verifies its checksum, and replaces the binary atomically. Database migrations run automatically on the next restart — or, for production and large databases, apply them as an explicit step with temps migrate before restarting.
Check your current version
temps --version
Output:
{TEMPS_VERSION} (abc1234) built 2026-06-17 12:34:56 UTC
Check for updates
See if a newer version is available without installing it:
temps upgrade --check
Or use the health check command, which includes an update check along with other system diagnostics:
temps doctor
If a newer version exists, you will see:
WARN Update: v1.1.0 available - run 'temps upgrade' to update
Pre-upgrade checklist
Run temps doctor before every upgrade. It surfaces the one thing that most
often goes wrong on a busy instance — pending database migrations on large
tables:
temps doctor
Look at the Migrations line:
PASS Migrations: 107 applied, up to date ← safe to upgrade
WARN Migrations: 107 applied, 3 PENDING — run `temps migrate` before restarting
If it reports PENDING, the new binary needs to apply schema changes. On a
small database these run in seconds. On a large one — for example a
proxy_logs request-log table with millions of rows — a migration that builds
an index can take several minutes. If that happens during server startup it
can exceed the boot migration window and leave the service in a restart loop.
The fix is simple and is the recommended flow below: apply migrations as an
explicit step with temps migrate, then restart the server. Because you run
it by hand with no time pressure, a slow migration just takes a while — it can
never crash-loop the API.
Quick way to gauge risk: check the size of your largest time-series tables.
docker exec temps-timescaledb psql -U postgres -d temps -c \
"SELECT count(*) FROM proxy_logs;"
Tens of millions of rows means index-building migrations may take minutes —
use the explicit temps migrate flow and expect it to run for a while.
Back up before upgrading
Back up before upgrading
- 1
Open the dashboard and go to Settings > Backups.
- 2
Click Run Backup to trigger a manual backup against your configured S3 backup storage.
- 3
If you do not have S3 storage configured, instead copy the data directory and dump the database from the shell: cp -r ~/.temps ~/.temps-backup-$(date +%Y%m%d) and docker exec temps-timescaledb pg_dumpall -U postgres | gzip > temps-db-backup-$(date +%Y%m%d).sql.gz.
Checkpoint: Confirm the backup includes ~/.temps/encryption_key -- without it you cannot decrypt stored service credentials.
Before any upgrade, create a backup of your data:
If you have S3 backup storage configured:
Trigger a manual backup from the dashboard (Settings > Backups > Run Backup) or via the API.
If you do not have S3 configured:
At minimum, back up the Temps data directory and database:
# Back up the data directory (contains encryption key, auth secret, GeoLite2 DB)
cp -r ~/.temps ~/.temps-backup-$(date +%Y%m%d)
# Back up the PostgreSQL database
docker exec temps-timescaledb pg_dumpall -U postgres | gzip > temps-db-backup-$(date +%Y%m%d).sql.gz
The ~/.temps/encryption_key file is critical — it encrypts managed service credentials. If this file is lost, you cannot decrypt stored passwords. Always include it in your backups.
Recommended upgrade flow
For production and any instance with a large database, apply migrations as an explicit step with the new binary before restarting the server. This is the safest sequence and the one we recommend for all multi-user installs:
# 1. Back up (see the section above)
# 2. Download + swap the new binary. `temps upgrade` replaces the binary in
# place; it does NOT restart the service, so the running server keeps using
# the old binary already loaded in memory until you restart it.
temps upgrade -y
# 3. Stop the server
sudo systemctl stop temps
# 4. Apply migrations explicitly with the new binary — no time limit, watch it complete
temps migrate --database-url="$TEMPS_DATABASE_URL"
# 5. Start the server (schema is already up to date, so boot is fast)
sudo systemctl start temps
# 6. Confirm
temps doctor
temps migrate connects, then prints the plan up front — the pending
migrations it is about to apply — and applies them one at a time, printing a
✓ line per migration with its name and how long it took, followed by a
summary. If a migration fails it stops there, marks it with ✗, shows the
error, and lists the ones it did not reach, so you know exactly where the run
stopped and the database's state. After a clean run it also performs any
continuous-aggregate backfill. Because it runs with the server stopped and no
startup timeout, a slow index build on a large table simply runs to completion
instead of risking a crash loop.
Running database migrations…
Pending migrations (3):
- m20260601_000009_metrics_caggs_keep_labels
- m20260601_000010_add_service_id_to_alarms
- m20260603_000001_create_otel_trace_summaries
Applying…
✓ m20260601_000009_metrics_caggs_keep_labels (44ms)
✓ m20260601_000010_add_service_id_to_alarms (8ms)
✓ m20260603_000001_create_otel_trace_summaries (1.10s)
✓ 3 migration(s) applied in 1.15s. Safe to restart the server.
When the database is already current it prints ✓ Database already up to date — no migrations to apply. and exits, so it is safe to run on every deploy.
Simple / single-node installs don't need the manual step — temps serve
applies pending migrations automatically on boot, exactly as before. The
explicit temps migrate flow matters when your database is large enough that
a migration could exceed the boot window, or when you want full control over
when schema changes happen.
Running migrations is safe to repeat and safe across versions: temps migrate
only applies migrations this binary defines that aren't already recorded.
Migration rows from a different/newer build (or Enterprise Edition) are left
untouched and never cause a failure.
Run the upgrade
Run the upgrade
- 1
These steps run on the server host (via SSH or a terminal on the machine), not in the web dashboard. Run temps upgrade (or sudo temps upgrade if the binary lives in a root-owned path like /usr/local/bin/temps); for production prefer the recommended flow that applies migrations explicitly first.
- 2
The upgrader detects your platform, fetches the latest GitHub release, and exits early if you are already up to date.
- 3
Review the upgrade plan it prints (current version, target version, download size) and confirm, or pass --yes / -y to skip the prompt.
Checkpoint: It verifies the SHA-256 checksum before swapping, then replaces the binary atomically -- the running server keeps the old binary in memory until you restart.
- 4
Restart the service to load the new binary: sudo systemctl restart temps.
The one-command path below is fine for simple installs. For production, prefer the recommended flow above.
Upgrade to the latest version
temps upgrade
If the binary is in a root-owned location (e.g. /usr/local/bin/temps):
sudo temps upgrade
The upgrade process:
- Detects your platform (Linux/macOS, x86_64/ARM64)
- Fetches the latest release from GitHub
- Compares versions — exits if already up to date
- Shows the upgrade plan (current version, target version, download size)
- Asks for confirmation
- Downloads the release tarball
- Verifies the SHA-256 checksum (if available)
- Replaces the binary atomically (write to temp file, then rename)
Options
# Upgrade to a specific version (ignores the channel)
temps upgrade --version v1.1.0
# Skip the confirmation prompt
temps upgrade --yes
# Track a release channel (stable is the default)
temps upgrade --channel beta
# Upgrade a binary at a different path
temps upgrade --path /opt/temps/bin/temps
| Flag | Default | Description |
|---|---|---|
--channel <stable|beta> | stable | Which release stream to track. See Release channels. |
--version <vX.Y.Z> | latest in channel | Pin a specific version. Bypasses channel filtering entirely. |
--path <path> | currently running binary | Path to the temps binary to replace. |
--yes, -y | off | Skip the confirmation prompt. |
--check | off | Preview the planned upgrade without installing. |
The old boolean --stable flag still works but is deprecated and hidden — it is an alias for --channel stable (which is the default anyway) and will be removed in a future release. Use --channel stable instead.
Other upgrade methods
temps upgrade is the recommended path because it verifies the checksum and swaps the binary atomically. These alternatives exist for installs that were set up with the deploy script or that prefer a one-shot command.
Upgrade script (deploy.sh installs)
If you installed Temps with deploy.sh, the upgrade script handles everything in one command — it backs up your encryption key, downloads the latest binary, and restarts the services:
curl -fsSL https://temps.sh/upgrade.sh | bash
To pin a specific version:
curl -fsSL https://temps.sh/upgrade.sh | bash -s vX.Y.Z
Re-run the installer
The installer downloads the latest binary for your platform and replaces the one at ~/.temps/bin/temps, preserving all configuration, data, and certificates. It accepts the same --channel flag as temps upgrade:
# Latest stable (default)
curl -fsSL https://temps.sh/install.sh | bash
# Latest beta
curl -fsSL https://temps.sh/install.sh | bash -s -- --channel beta
Then restart the service (see Restart Temps).
Manual download
Download a release tarball directly from GitHub, extract it, and replace the binary:
curl -LO https://github.com/gotempsh/temps/releases/latest/download/temps-linux-amd64.tar.gz
tar -xzf temps-linux-amd64.tar.gz
sudo mv temps /usr/local/bin/temps
sudo systemctl restart temps
To pin a version, swap latest/download for download/vX.Y.Z.
Release channels
temps upgrade tracks one of two release streams, selected with --channel:
| Channel | Selects | Notes |
|---|---|---|
stable (default) | Non-prerelease GitHub releases only | Tags without a - suffix, e.g. v1.2.0. |
beta | Both stable and prerelease releases | Includes tags like v1.2.0-beta.4 or v1.2.0-rc.1. |
Running temps upgrade with no flags always lands on stable.
Because GitHub returns releases newest-first and beta accepts both kinds, a beta host always upgrades to the freshest available version — including a newly shipped stable that supersedes the latest beta on the same line. The runtime decision is GitHub's prerelease flag on each release (draft releases are never selectable on either channel), not literal tag-string parsing.
Channel selection is CLI-only by design — there is no environment-variable fallback. You must pass --channel beta explicitly on each invocation to opt into prereleases. This prevents a long-lived shell or CI environment variable from silently switching a host onto beta.
Pinning a specific --version ignores the channel entirely and fetches that exact release directly.
The install.sh installer
The curl-pipe installer accepts the same --channel flag with matching semantics (default stable, accepts only stable or beta):
# Install the latest stable release (default)
curl -fsSL https://temps.sh/install.sh | bash
# Install the latest beta release
curl -fsSL https://temps.sh/install.sh | bash -s -- --channel beta
Stable resolves through GitHub's /releases/latest endpoint (which returns the newest non-prerelease); beta takes the newest tag from /releases?per_page=20. Passing an explicit version positional argument ignores the channel. One divergence from the Rust CLI: the bash installer does not filter draft releases — it trusts that the GitHub API only returns shipped releases, whereas the CLI explicitly skips drafts.
Restart Temps
The upgrade replaces the binary but does not restart the running process. You need to restart manually.
If running under systemd (typical server setup)
sudo systemctl restart temps
If you run the proxy as a separate process (split-process mode):
sudo systemctl restart temps-proxy
See Split-process upgrade (zero-downtime) for the recommended sequence when running in split-process mode — you can restart the console without touching the proxy, keeping traffic flowing the entire time.
If running in a terminal
Stop the current process (Ctrl+C) and start it again:
temps serve
Database migrations
There are two ways migrations get applied:
Automatic (on boot). When temps serve starts it connects to PostgreSQL,
applies any pending migrations (10-minute timeout), backfills new TimescaleDB
continuous aggregates, and then starts serving traffic. This is the default and
is fine for simple, single-node installs. If migrations fail, the server will
not start — check the logs for the specific migration error.
Manual (temps migrate). Apply pending migrations as an explicit step,
with the server stopped and no timeout:
temps migrate --database-url="$TEMPS_DATABASE_URL"
This is the recommended path for production and large databases — see Recommended upgrade flow. Running it before you restart means the server boots against an already-migrated schema, so a slow index build on a big table can't trip the startup timeout and cause a crash loop.
Both paths are safe across versions and safe to re-run: only the migrations this binary defines that aren't already recorded get applied. Migration rows written by a different build (a newer version, or the Enterprise Edition) are left untouched and never cause a failure — so a mixed or partially-rolled-back history won't block startup.
Split-process upgrade (zero-downtime)
In the default single-process setup, temps serve runs both the Pingora reverse proxy and the console (API + dashboard) inside a single OS process. A restart means a brief interruption for in-flight requests.
Split-process mode separates the two roles into independent processes:
temps proxy— the Pingora reverse proxy only. It forwards incoming traffic to your deployed apps. It does not depend on the console being alive.temps serve --split(ortemps-console.service) — the console process: REST API, dashboard, background jobs, database migrations. It does not handle incoming app traffic directly.
Because the proxy and the console are independent, you can restart the console alone to load a new binary and apply migrations while the proxy continues forwarding traffic uninterrupted. Users and deployed apps see zero downtime.
Starting in split-process mode
Pass --split to temps serve to start only the console role. The proxy must be started separately:
# Start the proxy (keeps running across console restarts)
temps proxy
# Start the console in split mode
temps serve --split
Under systemd, two separate units manage the processes:
| Unit | Role | Restart policy |
|---|---|---|
temps-proxy.service | Pingora reverse proxy — forwards app traffic | Rarely restarted |
temps-console.service | Console: API, dashboard, jobs, migrations | Restarted on every upgrade |
The temps-console.service unit should declare After=temps-proxy.service so systemd starts the proxy first on boot, but there is no hard dependency — the console comes up fine even if the proxy is temporarily down, and vice versa.
Version-skew handling
When you restart the console with a new binary but leave the proxy on the old one (or the reverse), there is a brief window where proxy and console are running different versions. Temps handles this gracefully:
-
The proxy reads its routing table from PostgreSQL on startup and reloads it in-process on demand. It does not call the console's API at runtime for routing decisions, so a version mismatch does not break traffic forwarding.
-
On startup, the console emits a warning in its logs if it detects the proxy's reported version differs from its own:
WARN version skew detected: console v0.1.1, proxy v0.1.0 — restart temps-proxy to clear -
Routing continues normally during the skew window. The proxy version reported in
temps doctorand the health endpoint reflects what the proxy process last registered. -
The safe skew window is one minor version (e.g. console
v0.1.1, proxyv0.1.0is fine). Spanning two or more minor versions is not tested and may cause incompatibilities in the coordination protocol.
If you see the skew warning after a console upgrade, plan to restart the proxy in the next low-traffic window using the sequence below.
Recommended zero-downtime upgrade sequence (split-process)
This sequence keeps the proxy forwarding traffic throughout the upgrade. The console is the only process that restarts, and it is down for only the seconds it takes to apply migrations and boot:
# 1. Back up (see the Back up before upgrading section)
# 2. Download and swap the new binary (does NOT restart anything)
temps upgrade -y
# 3. Stop the console only — the proxy keeps serving traffic
sudo systemctl stop temps-console
# 4. Apply migrations explicitly with the new binary (no startup timeout)
temps migrate --database-url="$TEMPS_DATABASE_URL"
# 5. Start the new console — schema is already current so boot is fast
sudo systemctl start temps-console
# 6. Confirm the console is healthy
temps doctor
# 7. (Optional) Restart the proxy to clear any version-skew warning
# Do this in a low-traffic window — the proxy reload takes a few seconds
sudo systemctl restart temps-proxy
Step 7 is optional if you are within the safe one-minor-version skew window and can schedule the proxy restart later.
If you also need to restart the proxy (step 7), the brief proxy restart is still far shorter than a full single-process restart that also runs migrations, because migrations already ran in step 4. App requests that arrive during the proxy restart will be refused at the TCP layer for the few seconds it takes to come back up. Plan this for a low-traffic period.
Verify the upgrade
After restarting:
# Check the version
temps --version
# Confirm the service is healthy
curl -sf http://localhost:8081/health
# Run diagnostics
temps doctor
The doctor command checks:
- Data directory integrity
- Encryption key presence
- Database connectivity and version
- TimescaleDB extension
- Migrations — how many are applied, and whether any are still pending (it tells you to run
temps migrateif so) - Docker daemon availability
- External connectivity (GitHub API, Docker Hub, Let's Encrypt)
- Git providers and DNS providers
If everything passes, your upgrade is complete. Visit the dashboard to confirm it loads correctly.
Upgrade the database
Apply database migrations explicitly
- 1
These steps run on the server host (via SSH or a terminal on the machine), not in the web dashboard. Stop the server first so migrations run with no startup timeout: sudo systemctl stop temps.
- 2
Apply pending schema migrations with the server binary: temps migrate --database-url="$TEMPS_DATABASE_URL". This is the server binary's schema migration command — not the @temps-sdk/cli platform-migration wizard. It prints the plan up front and applies each migration one at a time, printing a checkmark and timing per migration.
Checkpoint: Watch for the summary line -- e.g. "3 migration(s) applied. Safe to restart the server." -- or "Database already up to date" if there was nothing to apply.
- 3
Start the server again now that the schema is current, so boot is fast: sudo systemctl start temps.
- 4
Confirm everything is healthy with temps doctor and check the Migrations line reads "applied, up to date".
TimescaleDB runs as a Docker container and is upgraded independently of Temps:
# Pull the latest image
docker pull timescale/timescaledb-ha:pg18
# Stop and remove the old container (data persists in the volume)
docker stop temps-timescaledb
docker rm temps-timescaledb
# Start with the new image
docker run -d \
--name temps-timescaledb \
--restart unless-stopped \
-e POSTGRES_USER=temps \
-e POSTGRES_PASSWORD="YOUR_DB_PASSWORD" \
-e POSTGRES_DB=temps \
-p 127.0.0.1:5432:5432 \
-v temps-db-data:/home/postgres/pgdata/data \
timescale/timescaledb-ha:pg18
# Restart Temps to reconnect
sudo systemctl restart temps
Your database password is stored in ~/.temps/.wizard-state/db_password if you used the deploy script.
Switch to Enterprise Edition
Switch to Enterprise Edition
temps upgrade --tier ee switches a running install from the open-source binary (the default) to the Enterprise Edition (EE) binary. The EE binary lives in a license-gated proxy, so you must supply a license JWT.
temps upgrade --tier ee --license-path /path/to/license.jwt
EE currently ships linux-amd64 only. On any other platform the command fails early — macOS and arm64 EE builds are on the roadmap. The EE binary refuses to start without a valid license, so you must restart the service after switching.
EE flags
| Flag | Default | Description |
|---|---|---|
--tier <oss|ee> | oss | Which edition to install. oss downloads from GitHub releases. CLI-only — no env-var fallback. |
--license-path <path> | — | Path to the EE license JWT. Required with --tier ee. |
--ee-api <url> | https://temps.sh | Base URL of the Temps Cloud EE proxy (--tier ee only). A trailing slash is trimmed. |
--data-dir <path> | $TEMPS_DATA_DIR, else ~/.temps | Data dir whose data/license.jwt receives the license. Also reads the TEMPS_DATA_DIR env var. |
If you pass --tier ee without --license-path, the command errors with:
--tier ee requires --license-path <path-to-license.jwt>. Download yours from https://temps.sh/dashboard/license
What the switch does
- Validates the license JWT shape before downloading anything. The CLI decodes (but does not verify the signature — the EE binary checks the signature at boot) and rejects: malformed JWTs that are not three dot-separated segments, any
tierclaim that is notpremiumorenterprise, and licenses whoseexpclaim is already in the past. - Resolves the version — the pinned
--version, or the latest from<ee-api>/api/ee/releases. - Fetches the checksum and tarball from
<ee-api>/api/ee/download/<version>/<asset>/sha256and<ee-api>/api/ee/download/<version>/<asset>, authenticating with the license JWT as anAuthorization: Bearerheader. The asset name istemps-ee-<version-without-v>-linux-amd64.tar.gz. The checksum is verified before the swap. - Replaces the current binary atomically.
- Copies the license to
<data-dir>/data/license.jwt(mode0600). - Best-effort systemd wiring (Linux only): if
/etc/systemd/system/temps.serviceexists and does not already contain aTEMPS_EE_LICENSE_PATH=line, the upgrader insertsEnvironment=TEMPS_EE_LICENSE_PATH=<installed-license-path>after the[Service]header and runssystemctl daemon-reload, so the binary finds its license on every restart. If the variable is already present it is left untouched; non-Linux hosts and missing units are skipped silently.
Use --check to preview the planned switch without installing, and -y/--yes to skip the confirmation prompt. After the switch, restart the service to activate:
sudo systemctl restart temps
Rolling back
Roll back to a previous version
- 1
If the version you are leaving added database migrations, restore the database from your pre-upgrade backup first: docker exec -i temps-timescaledb psql -U temps temps < temps-backup-YYYYMMDD.sql (rolling back migrations is not automatic).
Checkpoint: Confirm the restore completed without errors before swapping the binary.
- 2
Downgrade the binary by pinning the target version: temps upgrade --version v1.0.0 (pinning a version bypasses channel filtering entirely).
- 3
Restart the service: sudo systemctl restart temps.
Downgrade to a specific version by passing it to the upgrade command:
temps upgrade --version vX.Y.Z
If the new version added database migrations, restoring from a pre-upgrade backup is the safest path:
# 1. Restore the database backup
docker exec -i temps-timescaledb psql -U temps temps < temps-backup-YYYYMMDD.sql
# 2. Downgrade the binary
temps upgrade --version vX.Y.Z
# 3. Restart
sudo systemctl restart temps
Rolling back database migrations is not automatically supported. If a new version added migrations, restoring the database from a pre-upgrade backup is the safest way to revert.
Troubleshooting
Service fails to start after upgrade
# Check logs for errors
sudo journalctl -u temps -n 50 --no-pager # Linux
tail -50 ~/.temps/logs/temps.log # macOS
# Common fix: ensure data directory permissions are correct
chmod 700 ~/.temps/data
chmod 600 ~/.temps/data/encryption_key
Database migration errors
Migrations run on startup and the server will not start if they fail. Confirm the database is reachable, then read its logs:
# Check that the database is reachable
docker exec temps-timescaledb pg_isready -U temps
# Check database logs
docker logs temps-timescaledb --tail 50
If the database container is not running, start it first:
docker start temps-timescaledb
"Database migrations timed out" / crash loop after upgrade
If the server log shows something like:
Database migrations timed out after 600 seconds
and the service keeps restarting, a migration is taking longer than the boot
window allows. This almost always means a new release added an index or schema
change to a large table — most commonly proxy_logs, which on a busy
instance can hold tens of millions of rows. The fix is to apply migrations with
the server stopped and no timeout, then restart:
# 1. Stop the service so it stops crash-looping (and releases the DB)
sudo systemctl stop temps
# 2. Apply migrations explicitly with the NEW binary — this has no timeout and
# will run a slow index build to completion. Expect minutes on a big table.
temps migrate --database-url="$TEMPS_DATABASE_URL"
# 3. Start the server — the schema is now up to date, so boot is fast
sudo systemctl start temps
# 4. Confirm
temps doctor
Following the recommended upgrade flow — run
temps migrate before restarting — avoids this situation entirely, because
migrations never run under the boot timeout in the first place.
If proxy_logs has grown far beyond what you need, you can shrink it before
migrating so the index build finishes faster. Check the size first:
docker exec temps-timescaledb psql -U temps -d temps \
-c "SELECT count(*) FROM proxy_logs;"
To drop old request-log data (this is analytics/observability data, not your application data), drop chunks older than a cutoff — for example keep the last 7 days:
docker exec temps-timescaledb psql -U temps -d temps \
-c "SELECT drop_chunks('proxy_logs', older_than => now() - interval '7 days');"
drop_chunks is far faster than DELETE because it removes whole TimescaleDB
chunks instead of scanning rows. Make sure no client is holding a lock on the
table (stop temps serve first) or the drop will block. Never kill the
TimescaleDB container to force this — stop the Temps service and let the drop
complete.