Skip to content

Notifications ​

Two independent channels: Gotify push and SMTP email. Enable either, both, or neither. Configuration is validated at startup, so a broken notification setup stops the service immediately rather than failing quietly the first time something interesting happens.

When notifications fire ​

EventPrioritySent
CPU or RAM scaled up7Only when the resource actually changed
CPU or RAM scaled down5Only when the resource actually changed
Error processing a VM9Every occurrence
Unexpected error in the main loop10Every occurrence, then 60 s pause

Nothing is sent for a routine poll, and nothing is sent when a threshold is crossed but no change results — hitting max_cores, or being inside a cooldown, is silent. Priority is a Gotify concept; the SMTP channel ignores it.

Error notifications can be a firehose

A node that has become unreachable produces a priority-9 notification per VM, per cycle. Twenty VMs on a 300-second interval is 240 notifications an hour. There is no rate limiting or deduplication. Consider Gotify-side filtering if this matters to you.

Gotify ​

yaml
gotify:
  enabled: true
  server_url: https://gotify.example.com
  app_token: AbCdEf123456789
  priority: 5
KeyRequiredNotes
enabledyesfalse disables the channel entirely
server_urlwhen enabledBase URL. A trailing slash is stripped for you
app_tokenwhen enabledApplication token, not a client token
prioritynoDefault priority; per-event values above override it

Messages POST to {server_url}/message with a 10-second timeout, Authorization: Bearer <app_token>, and the title VM Autoscale Alert.

If enabled: true and either server_url or app_token is missing, startup fails with:

ConfigurationError: Gotify is enabled but configuration is incomplete

Getting a token ​

In Gotify: Apps → Create Application, then copy the token shown. Verify it out of band before wiring it in:

bash
curl -X POST "https://gotify.example.com/message" \
  -H "Authorization: Bearer AbCdEf123456789" \
  -F "title=test" -F "message=hello" -F "priority=5"

Email (SMTP) ​

yaml
alerts:
  email_enabled: true
  email_recipient: ops@example.com
  smtp_server: smtp.example.com
  smtp_port: 587
  smtp_user: autoscale@example.com
  smtp_password: your_smtp_password
KeyRequiredNotes
email_enabledyes
smtp_serverwhen enabledValidated at startup
smtp_userwhen enabledAlso used as the From address
email_recipientwhen enabledA string, or a list of strings for several recipients
smtp_portnoDefaults to 587
smtp_passwordnoLeave empty to skip login() — for relays that authenticate by IP

The service always calls starttls(), then authenticates only if smtp_password is non-empty.

Subject lines ​

The VMID is extracted from the message body with the pattern VM\s+(\d+), producing:

Subject: VM Autoscale Alert for VM 101

Messages with no VMID in them — most main-loop errors — get VM Autoscale Alert for VM with a trailing space. Cosmetic, but it makes subject-based mail filters unreliable for those.

Several recipients ​

yaml
alerts:
  email_recipient:
    - ops@example.com
    - oncall@example.com

Local relay ​

If the host already has a working MTA:

yaml
alerts:
  email_enabled: true
  smtp_server: localhost
  smtp_port: 25
  smtp_user: vm-autoscale@$(hostname -f)
  smtp_password: ""
  email_recipient: root@example.com

Note starttls() is called unconditionally, so a relay on port 25 must advertise STARTTLS. A plain-text-only local relay will fail.

Failure handling ​

Channels are attempted independently and a failure in one does not block the other. Failures are logged, not retried, and never crash the service:

[ERROR] Failed to send Gotify notification: HTTPSConnectionPool(...): Read timed out.

If every configured channel fails, the message itself is written to the log so it is not lost:

[WARNING] Failed to send notification through any channel.
          Message: Scaled up CPU for VM 101 due to high usage (91.2%).
          Errors: Failed to send Gotify notification: ...

With no channel enabled at all, you get one warning at startup and then silence:

[WARNING] No notification method is enabled in configuration

Verifying without waiting for load ​

Temporarily set an impossible threshold, run the service in the foreground for one cycle, and put it back:

yaml
scaling_thresholds:
  cpu:
    high: 1
    low: 0
bash
sudo systemctl stop vm_autoscale.service
sudo python3 /usr/local/bin/vm_autoscale/autoscale.py

Security notes ​

smtp_password and app_token sit in plain text in config.yaml. That file must be mode 600 and root-owned — the installer does this, and the hardening guide covers verifying it. Notification bodies contain VMIDs, host names and usage figures; treat your Gotify server and mail path as carrying operational detail about your infrastructure.