Ratatouille is a React-inspired TUI framework for Elixir that runs on top of ExTermbox, letting you build interactive terminal applications with a declarative component model — views, event loops, and state management in pure Elixir. Whether you're building CLI dashboards, admin tools, or interactive DevOps utilities, Ratatouille applications run as long-lived BEAM processes that depend on external services, background tasks, and system resources. Vigilmon gives you the uptime monitoring, heartbeat checks, and alerting to catch failures in those dependencies before your users hit broken interfaces.
What You'll Set Up
- HTTP health endpoint exposing the application's readiness and external dependency status
- Cron heartbeat to confirm background data-fetching workers are running
- Latency alerts for slow upstream services that feed the TUI's display data
- SSL certificate monitoring for any HTTPS endpoints the application polls
Prerequisites
- Elixir 1.14+ with
ratatouilleandex_termboxin yourmix.exs - A Plug- or Bandit-based HTTP server (even minimal) for the health endpoint
- A free Vigilmon account
Step 1: Add a Health Endpoint Alongside Your TUI Process
Ratatouille applications are typically supervised OTP applications. Add a minimal Plug-based health endpoint as a sibling process under your supervisor so Vigilmon can reach it independently of the terminal UI:
# mix.exs
defp deps do
[
{:ratatouille, "~> 0.5"},
{:bandit, "~> 1.0"},
{:plug, "~> 1.15"},
# ... other deps
]
end
# lib/my_app/health_router.ex
defmodule MyApp.HealthRouter do
use Plug.Router
plug :match
plug :dispatch
get "/health" do
checks = %{
data_worker: check_data_worker(),
upstream_api: check_upstream_api()
}
status = if Enum.all?(checks, fn {_, v} -> v == :ok end), do: 200, else: 503
conn
|> put_resp_content_type("application/json")
|> send_resp(status, Jason.encode!(%{status: if(status == 200, do: "ok", else: "degraded"), checks: checks}))
end
defp check_data_worker do
case Process.whereis(MyApp.DataWorker) do
nil -> :error
pid -> if Process.alive?(pid), do: :ok, else: :error
end
end
defp check_upstream_api do
case :httpc.request(:head, {'https://api.example.com/ping', []}, [{:timeout, 3000}], []) do
{:ok, {{_, 200, _}, _, _}} -> :ok
_ -> :error
end
end
end
Register it in your supervision tree:
# lib/my_app/application.ex
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
{Bandit, plug: MyApp.HealthRouter, port: 4001},
MyApp.DataWorker,
{Ratatouille.Runtime.Supervisor, runtime: [app: MyApp.TuiApp]}
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
Verify the health endpoint independently of the terminal UI:
curl http://localhost:4001/health
# {"status":"ok","checks":{"data_worker":"ok","upstream_api":"ok"}}
Step 2: Add the Monitor in Vigilmon
- Log in to vigilmon.online and click Add Monitor.
- Set Type to
HTTP / HTTPS. - Enter
http://your-server:4001/health(or your public health URL). - Set Check interval to
1 minute. - Set Expected HTTP status to
200. - Under Response body, enable Contains keyword and enter
"ok". - Click Save.
The keyword check detects the case where the health route returns 200 but reports a degraded downstream service — common when an upstream API becomes unreachable while the TUI process itself stays alive.
Step 3: Heartbeat Monitoring for the Data Fetching Worker
Ratatouille TUIs display live data — metrics dashboards, log tails, API monitors. The background workers that fetch and push updates to the runtime state can fail silently: the BEAM supervisor restarts them, but if the restart fails or the upstream is permanently unavailable, your TUI just shows stale data with no alert.
Add a Vigilmon cron heartbeat:
- In Vigilmon, click Add Monitor → Cron Heartbeat.
- Set Expected ping interval to match your worker's poll cycle (e.g.
60seconds for a 1-minute refresh). - Copy the heartbeat URL (e.g.
https://vigilmon.online/heartbeat/abc123).
Ping Vigilmon at the end of each successful data fetch:
# lib/my_app/data_worker.ex
defmodule MyApp.DataWorker do
use GenServer
require Logger
@interval 60_000
def start_link(_), do: GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
@impl true
def init(state) do
schedule_fetch()
{:ok, state}
end
@impl true
def handle_info(:fetch, state) do
case fetch_data() do
{:ok, data} ->
Ratatouille.Runtime.subscribe(MyApp.TuiApp, {:data_updated, data})
ping_heartbeat()
{:error, reason} ->
Logger.error("DataWorker fetch failed: #{inspect(reason)}")
# No ping → Vigilmon alerts after the window expires
end
schedule_fetch()
{:noreply, state}
end
defp schedule_fetch, do: Process.send_after(self(), :fetch, @interval)
defp ping_heartbeat do
heartbeat_url = System.get_env("VIGILMON_HEARTBEAT_URL")
if heartbeat_url do
:httpc.request(:get, {String.to_charlist(heartbeat_url), []}, [{:timeout, 5000}], [])
end
end
defp fetch_data do
# Your data fetching logic here
{:ok, %{}}
end
end
If the worker crashes and the supervisor's restart budget is exhausted, Vigilmon alerts you within one missed interval — no more silent stale dashboards.
Step 4: Alert on Slow Upstream API Responses
TUI dashboards are only as fast as their data sources. Add a dedicated Vigilmon HTTP monitor for each upstream API your Ratatouille app polls:
- Click Add Monitor → HTTP / HTTPS.
- Enter the upstream API URL (e.g.
https://api.example.com/data). - Set Check interval to
1 minute. - Under Advanced, set Response time threshold to your SLA (e.g.
2000 ms). - Click Save.
This catches upstream slowdowns before they cascade into unresponsive TUI updates. If the API starts responding in 4 seconds instead of 200 ms, Vigilmon alerts you while users are still seeing a sluggish — not yet broken — interface.
You can also use Telemetry in your data worker to emit latency measurements:
defp fetch_data_with_telemetry do
start = System.monotonic_time()
result = fetch_data()
duration = System.monotonic_time() - start
:telemetry.execute(
[:my_app, :data_worker, :fetch],
%{duration: duration},
%{result: elem(result, 0)}
)
result
end
Step 5: Monitor SSL Certificates for Upstream HTTPS Endpoints
If your Ratatouille app polls HTTPS APIs, add certificate expiry monitoring:
- Open the upstream API monitor from Step 4.
- Enable Monitor SSL certificate.
- Set Alert when certificate expires in less than
21 days. - Click Save.
Expired upstream certificates cause your data worker to receive TLS errors rather than data — which means your TUI shows stale values with no obvious reason why.
Step 6: Configure Alert Channels
- Go to Alert Channels in Vigilmon and add Slack, email, or PagerDuty.
- Set Consecutive failures before alert to
2to filter transient blips. - Use Maintenance windows when you're deliberately restarting the Ratatouille process for a new release:
curl -X POST https://vigilmon.online/api/maintenance \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{"monitor_id": "abc123", "duration_minutes": 10}'
Consider routing data-worker heartbeat failures to a high-priority channel (PagerDuty) while routing upstream latency warnings to a lower-priority Slack channel — the severity differs significantly.
Summary
| Monitor | Target | What It Catches |
|---|---|---|
| HTTP health check | /health on port 4001 | DataWorker crash, upstream API unreachable |
| Cron heartbeat | Heartbeat URL | Silent data-fetch failure, stale TUI state |
| Upstream HTTP monitor | External API URL | Slow or unavailable data source |
| SSL certificate | Upstream HTTPS endpoint | TLS expiry causing fetch errors |
Ratatouille's declarative model makes it easy to build rich terminal applications — Vigilmon makes sure the processes and services behind those applications stay alive and responsive. With a health endpoint, heartbeat monitoring, and upstream latency alerts, you'll know about failures before your dashboard shows stale data.