Health checks that actually tell you something

A verified /health route for external monitors, plus the honest truth about what Falix Schedules can and can't watch — and how to get automatic restarts.

"Is it up?" has two different answers: the process is running, and the app is actually answering requests. Those aren't the same — an app can be "running" while wedged and serving nothing. A /health route answers the second, honest question. This guide builds one (verified on the real runtime) and wires a realistic health story around it, including where Falix genuinely helps and where it honestly doesn't.

At a glance
You need A web app on Falix (Node.js or Python)
Plan Any for the route; automatic restarts lean on premium (explained below)
Time Thirty minutes

Why "the process is alive" isn't enough

The panel marks your server online when it spots the ready line in the console (for Node, a line containing Listening). That's a process signal — it means your code started, not that it's healthy. A server can sit at "online" while its event loop is blocked, its database handle is dead, or every request 500s.

A /health route closes that gap. It's a real HTTP request that only succeeds if your app can actually handle a request right now. That's a stronger promise than "the process didn't exit".

Build a /health route

Keep it cheap and fast — a bare health check just proves the app responds:

const http = require('http');
const PORT = process.env.SERVER_PORT || 8080;
const startedAt = Date.now();

const server = http.createServer((req, res) => {
  if (req.url === '/health') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ status: 'ok', uptime: Math.round((Date.now() - startedAt) / 1000) }));
    return;
  }
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello from Falix!');
});

server.listen(PORT, '0.0.0.0', () => console.log(`Listening on port ${PORT}`));

A request to /health returns 200 with {"status":"ok","uptime":12}. The same idea in Python/Flask (put flask in requirements.txt):

import os, time
from flask import Flask, jsonify

app = Flask(__name__)
started_at = time.time()

@app.route("/health")
def health():
    return jsonify(status="ok", uptime=round(time.time() - started_at))

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=int(os.environ.get("SERVER_PORT", 8080)))

Shallow vs deep

The routes above are shallow — "I can answer". If your app is useless without its database, make a deep check that pings the dependency and returns 503 when it's down, so a monitor sees the outage:

if (req.url === '/health') {
  try {
    await db.query('SELECT 1');          // your real dependency
    res.writeHead(200); res.end('{"status":"ok"}');
  } catch (e) {
    res.writeHead(503); res.end('{"status":"degraded"}');
  }
  return;
}

Keep it lightweight either way — a monitor hits it every minute, so it shouldn't do heavy work.

Who watches it? The honest part

A route on its own does nothing until something calls it. Here's the truth about what does the calling.

An external uptime monitor (this is the HTTP-probe layer)

The thing that hits your /health on a timer lives outside Falix: an uptime service (UptimeRobot, BetterStack and friends) or your own status page. You give it http://your-address:port/health, it checks every minute, and it alerts you the moment the check fails. This works on any plan, because the monitor is external — Falix just serves the route.

⚠️ Falix does not probe your /health for you. There is no panel feature that calls an HTTP URL on a schedule and restarts on a bad response. Health probing is the external monitor's job. Don't design around a Falix HTTP-probe — it doesn't exist.

What Falix Schedules actually do

Schedules are powerful, but they react to the server's own signals, not to HTTP responses. Here's the real surface:

Schedules CAN trigger on Schedules CAN'T do
Server started / stopped / crashed Make an HTTP request to your /health
CPU / memory / disk usage crossing a threshold Read your app's 200/503 reply
Server idle, backup completed, player joined/left Restart based on a route's response
Interval / daily / weekly / monthly / manual / git-deploy

And a schedule's tasks are: command, power action (start/restart/stop/kill), backup, delete files, git sync. So Schedules give you the react-and-restart layer — driven by crashes and resource thresholds — not the probing layer.

The honest health story, in layers

Put the pieces together and stop trying to make one tool do everything:

  1. The route/health, on any plan. The source of truth for "can this app answer?"
  2. The external monitor — hits /health, pings you when it fails. Any plan (it's an outside service). This is how you find out something's wrong.
  3. Automatic restart — how the server recovers on its own:
    • An Event schedule on server crashedPower action → Start brings a crashed server back up. (The power-action task and running while offline are premium in practice — see Schedules.)
    • An Event schedule on a CPU or memory threshold → restart or a warning command, to catch a runaway before it's killed.
    • Premium Crash Detection (Settings → Crash Detection tab) bundles auto-restart and health monitoring; the premium Monitoring page adds a health score and crash history. See The Settings page.

🎯 Free-plan reality: On free, power-action tasks and offline schedules are premium, and the session timer stops the server regardless. The dependable free health story is: a /health route + an external monitor that notifies you, then you restart by hand from the Console. Automatic self-healing is where premium earns its keep. See Keeping apps online.

Verify it works

Start the server and request the route from a browser or terminal:

curl http://your-address:port/health
# {"status":"ok","uptime":12}

A 200 with your JSON means the route is live. Point your external monitor at that URL and watch it go green — then stop the server and confirm the monitor notices and alerts you. That alert firing is the whole system working.

Troubleshooting

  • /health returns 200 but the app is still broken — your check is shallow. Make it deep so it actually tests the dependency that's failing.
  • The monitor can't reach /health — a port or bind problem: read SERVER_PORT and bind 0.0.0.0, and use the exact address from the Network page. See I can't reach my app.
  • I expected Falix to restart on a bad /health — it can't; nothing in the panel probes HTTP. Use an external monitor to alert you, and a server crashed or resource threshold schedule (premium power action) to restart.
  • The server keeps getting killed under load — that's memory, not a health-route issue. See Out of memory.

Next steps

Was this guide helpful?