Troubleshooting — Common Errors and Fixes
Most Temps problems leave a clear signal in the logs — start there. Common problems and how to fix them. If you don't find your issue here, check the build logs first — they contain the exact error.
Start here. Pick the line that matches what you're seeing:
deploy.shstopped with an error, or the console has no HTTPS → Installer errors- Deploy failed during build → Debug a failed deployment
- App won't start / container exits immediately → Deployment Errors
- 502 Bad Gateway or 504 Gateway Timeout → Runtime Errors
- Domain or HTTPS not working → SSL / Domain Issues
temps upgradefails → Upgrade errors- Not sure what's wrong → read the logs first
Reading logs
Before trying anything else, check the logs:
- Build logs — Project → Deployments → [deploy] → Build Log
- Runtime logs — Project → Logs — stdout/stderr from the running container
- System logs — SSH into your server:
journalctl -u temps -f
See Logs for filtering, retention and how build, runtime and proxy logs differ. The CLI commands below are documented in the CLI reference.
# Stream runtime logs via CLI
bunx @temps-sdk/cli runtime-logs --project my-app --follow
# View last 100 lines
bunx @temps-sdk/cli runtime-logs --project my-app --tail 100
Installer errors
These are the messages deploy.sh prints when an install stops. The installer is idempotent: fix the cause, then re-run the same command and it resumes from the step that failed. See the Quickstart for the install flow.
"This script requires bash, which was not found"
The installer is a bash script. Started from sh, dash or zsh it re-launches itself under bash, so this message means bash is not installed at all, which happens on minimal server images. Install it and re-run:
apt-get install -y bash # Debian / Ubuntu
dnf install -y bash # RHEL and derivatives
bash deploy.sh
"Interactive prompt '…' needs a terminal"
A prompt had no terminal to read from: the script ran over SSH without a TTY, in CI, or from an agent. Either give it a terminal or answer every prompt up front:
# Interactive, over SSH
ssh -t root@203.0.113.1 'bash /tmp/deploy.sh'
# Headless
bash deploy.sh --mode quick --email you@example.com --yes
Advanced mode can only run headless with --domain, --email and a --dns-provider (cloudflare, route53 or digitalocean) plus its credential. Without a DNS provider, the manual DNS-01 step pauses while you add a TXT record, so it needs a terminal.
"Quick mode with --yes requires --email"
Let's Encrypt needs a contact address and the installer has no fallback for it. Add --email:
bash deploy.sh --mode quick --email you@example.com --yes
"Docker installed but failed to start"
The Docker packages installed, but the daemon did not come up. Check why, then re-run the installer:
sudo systemctl status docker
sudo journalctl -u docker -n 50 --no-pager
If the message is "Docker installation failed" instead, the package install itself failed; the output is in /tmp/temps-wizard-cmd-out.log.
"Cannot continue without a free host port for the database"
The installer runs TimescaleDB in a container on host port 5432. If that port is taken (usually by a PostgreSQL already on the host) it moves to the next free port from 5433 to 5452 on its own. This error means every port in that range is in use. Find what is listening, free one port, and re-run:
sudo ss -ltnp | grep -E ':54[3-5][0-9]'
"The console HTTPS certificate did not provision" (console only reachable over HTTP)
In QuickStart mode the console and each app get a Let's Encrypt certificate the first time their hostname is opened. Let's Encrypt has to reach your server to issue it, so this almost always means inbound port 80 or 443 is blocked:
- Open TCP 80 and 443 in your provider's firewall or security group, and in
ufw/firewalldif you run one - Behind a router or NAT, forward ports 80 and 443 to this machine. The installer warns "you're likely behind a router/NAT" when the public IP it detects is not the machine's own address
- Reload the console URL. The certificate is requested again on the next visit
To see the exact reason, read the issuance log:
sudo journalctl -u temps --no-pager -n 400 | grep on-demand
If the installer reports "Let's Encrypt rate limit" instead, wait and reload, or use your own domain with Advanced mode.
Build errors
npm install fails — ENOTFOUND or registry timeout
The build container can't reach the npm registry:
- Check outbound internet access:
curl -I https://registry.npmjs.org - If behind a firewall, add the registry to the allowlist
- Consider a private registry or caching proxy
Cannot find module after install
A package listed in devDependencies is used at runtime — move it to dependencies. Or your .dockerignore excludes files that are needed at runtime.
Build exceeds memory limit
# Increase Node.js heap in the build step
RUN NODE_OPTIONS="--max-old-space-size=4096" npm run build
Or increase the limit under Project → Settings → Build Resources.
Multi-stage build — COPY --from file not found
The source path must match exactly what the builder stage produced. Add RUN ls /app in the builder stage to verify paths.
Deployment errors
Container starts then immediately exits
The container is crashing on startup. Check runtime logs. Common causes:
- Missing environment variable — app throws on startup if a required var is absent
- Port mismatch — app binds to
127.0.0.1instead of0.0.0.0 - Wrong start command —
CMDin the Dockerfile doesn't match your entry point
Health check failing
- Check Project → Logs for startup errors
- Temporarily disable the health check to confirm the app starts at all
- Ensure the health check path exists and doesn't require authentication
Deployment stuck at "Waiting for container"
# Redeploy via CLI
bunx @temps-sdk/cli deploy --project my-app
Runtime errors
502 Bad Gateway
The proxy can't reach your container:
- Container crashed — check logs for a panic or uncaught exception
- App is listening on the wrong port — ensure
PORTenv var is used and bound to0.0.0.0 - Out of memory — check Project → Metrics for OOM restarts, then raise the memory limit (see Resources and Scaling)
504 Gateway Timeout
Container reachable but not responding within 30s:
- Slow database query — enable slow query logging and add indexes
- External API call blocking the event loop
- CPU-bound work on the main thread — offload to a worker
Environment variables not updating
Variables set after the last deploy are not live until you trigger a new deploy.
SSL / domain issues
Certificate not provisioning
Let's Encrypt requires port 80 to be publicly reachable for the HTTP-01 challenge (see Domains & SSL for how certificates are issued and renewed):
- Confirm DNS A record points to the correct server IP
- Test port 80:
curl -I http://yourdomain.com - Ensure no CDN is intercepting with full-proxy mode enabled
Retry from Project → Settings → Domains → [domain] → Retry.
ERR_TOO_MANY_REDIRECTS
Your app is issuing an HTTP redirect when the proxy already handles HTTPS. Trust the X-Forwarded-Proto header:
// Express
app.set('trust proxy', 1);
DNS not propagating
Propagation can take up to 48 hours (usually under 10 minutes with a low TTL). Check at dnschecker.org.
"No deployment is routed to this host" (branded 404)
If you open a domain and see a Temps-branded page titled "No deployment is routed to this host" with a DEPLOYMENT_NOT_FOUND · 404 chip, the proxy received your request but no app is bound to that host. The page shows the requested Host and a Request ID for support log correlation.
This is expected in two situations:
- DNS still propagating — you just pointed a domain at the server, but the host isn't yet associated with a deployment. Add the domain under Project → Settings → Domains (see Add a custom domain) and wait for propagation.
- Hitting the server by an unrecognized hostname — for example the raw server IP or a stale CNAME. The proxy serves this 404 specifically when the host has no route and the request comes from a non-admin client (the admin gate keeps the management console invisible from such hosts).
The response is HTTP 404 with these headers:
| Header | Value |
|---|---|
Content-Type | text/html; charset=utf-8 |
Cache-Control | no-store |
X-Request-ID | the request ID shown on the page |
When filing a support request, include the Request ID from the page (or the X-Request-ID response header) — it matches the proxy log line admin gate denied request to non-admin host. To fix it, confirm the domain is added to a project and that DNS resolves to your server.
Database issues
Connection refused to localhost:5432
Your app is connecting to localhost but the database is in a separate container. Link the service to your project and use the connection string Temps injects (POSTGRES_URL for PostgreSQL) instead. See Managed PostgreSQL.
Too many connections
Each replica opens its own pool. With many replicas, you exceed PostgreSQL's max_connections (default 100). Reduce pool size per replica:
const pool = new Pool({ connectionString: process.env.POSTGRES_URL, max: 5 });
Or add PgBouncer as a connection pooler.
Upgrade errors
"No stable releases found"
Every Temps release so far is a v0.1.0 prerelease, so the default stable channel of temps upgrade has nothing to install. On v0.1.0-beta.56 and earlier the command also only reads the newest 20 GitHub releases, which are currently all nightly builds, so --channel beta fails too, with "No releases found.". Pin the release you want instead:
temps upgrade --version v0.1.0-beta.56
A pinned --version skips the channel lookup entirely. temps upgrade --channel nightly also works if you want daily builds from main. See Upgrade Temps for the full upgrade flow and how to roll back.
Getting more help
# Run diagnostics
temps doctor
Post in the Discord community with the diagnostics output attached. Questions about servers, pricing, upgrades and backups are answered in the FAQ.