General Proxmox VE Proxmox Backup Server Portainer Dockge Uptime Kuma Beszel Dell iDRAC HP iLO Pi-hole AdGuard Home Remote Access Local Network Discovery Cloudflare Access Tailscale OPNsense Unraid TrueNAS Synology DSM Nginx Proxy Manager SSL and Certificates
General
What is demo mode?

Demo mode lets you explore the full app without connecting to any real servers. It populates every screen with realistic mock data so you can see exactly what CtrlFreak looks like with a working homelab.

To enable it: open the app for the first time and tap Try Demo, or go to Settings and toggle Demo Mode on.

Demo mode is read-only. Actions like starting a VM or restarting a container are simulated locally and have no effect on real infrastructure.

How do I import my server configuration?

CtrlFreak supports importing a JSON configuration file so you don't have to enter API tokens and addresses one field at a time on your phone keyboard.

  1. Create a JSON file with your server details (see the format in Settings under Import JSON)
  2. Go to Settings and tap Import JSON
  3. Paste in the JSON and tap Save

All credentials are stored encrypted in the OS keychain after import and the raw JSON is discarded.

What does the JSON config template look like?

Copy the template below, fill in your server details, and import it in Settings > Import JSON. Remove any integrations you don't use. The id fields can be any unique string.

If any of your services sit behind Cloudflare Access, add cfAccessClientId and cfAccessClientSecret to that server's entry (or portainerCfAccessClientId / uptimeKumaCfAccessClientId at the top level for those flat-keyed integrations). Both fields must be set together. See the Cloudflare Access section above for details.

ctrlfreak-config.json
{
  "proxmoxServers": [
    {
      "id": "proxmox-1",
      "label": "My Proxmox",
      "host": "https://192.168.1.38:8006",
      "token": "user@pam!tokenname=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "verifySSL": false,
      "cfAccessClientId": "",
      "cfAccessClientSecret": ""
    }
  ],
  "portainerHost": "http://192.168.1.73:9000",
  "portainerToken": "your-portainer-api-token",
  "portainerVerifySSL": false,
  "portainerCfAccessClientId": "",
  "portainerCfAccessClientSecret": "",
  "uptimeKumaHost": "http://192.168.1.73:3001",
  "uptimeKumaUser": "",
  "uptimeKumaPass": "",
  "uptimeKumaVerifySSL": false,
  "uptimeKumaCfAccessClientId": "",
  "uptimeKumaCfAccessClientSecret": "",
  "idracServers": [
    {
      "id": "idrac-1",
      "label": "My Server",
      "host": "https://192.168.1.51",
      "username": "root",
      "password": "your-idrac-password",
      "verifySSL": false
    }
  ],
  "piholeServers": [
    {
      "id": "pihole-1",
      "label": "Pi-hole",
      "host": "http://192.168.1.100",
      "token": "your-pihole-api-token",
      "verifySSL": false
    }
  ],
  "tailscaleTailnet": "your-tailnet-name",
  "tailscaleApiKey": "tskey-api-xxxxxxxxxxxx",
  "tailscaleApiUrl": "",
  "unraidServers": [
    {
      "id": "unraid-1",
      "label": "Unraid Tower",
      "host": "http://192.168.1.52",
      "apiKey": "your-unraid-api-key",
      "verifySSL": false
    }
  ],
  "npmServers": [
    {
      "id": "npm-1",
      "label": "Nginx Proxy Manager",
      "host": "http://192.168.1.73:81",
      "email": "admin@example.com",
      "password": "your-npm-password",
      "verifySSL": false
    }
  ],
  "adGuardServers": [
    {
      "id": "adguard-1",
      "label": "AdGuard Home",
      "host": "http://192.168.1.53:3000",
      "username": "admin",
      "password": "your-adguard-password",
      "verifySSL": false
    }
  ],
  "pbsServers": [
    {
      "id": "pbs-1",
      "label": "Proxmox Backup Server",
      "host": "https://192.168.1.38:8007",
      "token": "user@realm!tokenname=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "verifySSL": false
    }
  ],
  "dockgeServers": [
    {
      "id": "dockge-1",
      "label": "Dockge",
      "host": "http://192.168.1.73:5001",
      "username": "admin",
      "password": "your-dockge-password",
      "verifySSL": false
    }
  ],
  "beszelServers": [
    {
      "id": "beszel-1",
      "label": "Beszel",
      "host": "http://192.168.1.73:8090",
      "username": "you@example.com",
      "password": "your-beszel-password",
      "verifySSL": false
    }
  ],
  "trueNASServers": [
    {
      "id": "truenas-1",
      "label": "TrueNAS",
      "host": "http://192.168.1.52",
      "apiKey": "your-truenas-api-key",
      "verifySSL": false
    }
  ],
  "opnsenseServers": [
    {
      "id": "opnsense-1",
      "label": "OPNsense",
      "host": "https://192.168.1.1",
      "apiKey": "your-api-key",
      "apiSecret": "your-api-secret",
      "verifySSL": false
    }
  ],
  "hpiloServers": [
    {
      "id": "hpilo-1",
      "label": "HP iLO",
      "host": "https://192.168.1.51",
      "username": "Administrator",
      "password": "your-ilo-password",
      "verifySSL": false
    }
  ],
  "synologyServers": [
    {
      "id": "synology-1",
      "label": "Synology NAS",
      "host": "http://192.168.1.54:5000",
      "username": "admin",
      "password": "your-dsm-password",
      "verifySSL": false
    }
  ],
  "wolDevices": [
    {
      "id": "wol-1",
      "name": "My Server",
      "mac": "AA:BB:CC:DD:EE:FF",
      "broadcastIp": "192.168.1.255"
    }
  ],
  "wolRelayUrl": "",
  "notifications": {
    "enabled": false,
    "pollIntervalMinutes": 15,
    "failureThreshold": 2,
    "notifyVMDown": true,
    "notifyContainerDown": true,
    "notifyServiceDown": true,
    "notifyIDRACCritical": true
  },
  "enabledIntegrations": {
    "proxmox": true,
    "portainer": true,
    "uptimeKuma": true,
    "idrac": true,
    "pihole": true,
    "tailscale": true,
    "unraid": true,
    "nginxProxyManager": true,
    "adGuardHome": false,
    "proxmoxBackupServer": false,
    "dockge": false,
    "beszel": false,
    "truenas": false,
    "opnsense": false,
    "hpilo": false,
    "synology": false
  },
  "appLockTimeoutMinutes": 15,
  "demoMode": false
}
Leave tailscaleApiUrl and wolRelayUrl as empty strings unless you have a custom setup. Arrays like proxmoxServers support multiple entries.
What is free vs Pro?

Demo mode (always free): Explore all 16 integrations with realistic sample data. No account or server required, no time limit.

7-day free trial: Connect your real servers and use everything. Starts automatically when you add your first integration.

Pro ($9.99 one-time): Full access to all 16 integrations with your real infrastructure after the trial ends. No subscription, no renewal, no tricks.

Where are my credentials stored?

All server credentials, API tokens, and passwords are stored in the OS secure keychain (iOS Keychain / Android Keystore). They are encrypted at rest and never leave your device.

CtrlFreak has no backend server. There is no developer cloud, no relay, no telemetry. Every request goes directly from your phone to your servers.

Proxmox VE
How do I create a Proxmox API token?
  1. Log into your Proxmox web UI
  2. Go to Datacenter > Permissions > API Tokens
  3. Click Add
  4. Select a user (e.g. root@pam) or create a dedicated user
  5. Give the token a name (e.g. ctrlfreak)
  6. Uncheck Privilege Separation if you want the token to inherit full permissions, or leave it on and assign specific roles
  7. Click Add and copy the token immediately. It will only be shown once.

The token format is: user@realm!tokenname=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

The host field should be your Proxmox URL including port, e.g. https://192.168.1.38:8006
What permissions does the Proxmox token need?

For read-only access: PVEAuditor role on /.

For full control (start/stop VMs, etc.): PVEAdmin role on /, or PVEVMAdmin on /vms if you want to limit scope.

To assign roles: Datacenter > Permissions > Add > API Token Permission.

Can I connect to multiple Proxmox nodes or clusters?

Yes. You can add multiple Proxmox servers in Settings and the dashboard aggregates data from all of them. Each server entry has its own host, token, and label.

If your nodes are in a cluster, you only need to add one node since the API returns data for all nodes in the cluster.

Portainer
How do I get a Portainer API token?
  1. Log into your Portainer instance
  2. Click your username in the top right and go to My account
  3. Scroll to Access tokens
  4. Click Add access token, give it a name, and copy it

The host should be your Portainer URL without a trailing slash, e.g. http://192.168.1.73:9000

Uptime Kuma
How do I find my Uptime Kuma status page slug?

The slug is the short name at the end of your status page URL.

  1. Log into Uptime Kuma
  2. Go to Status Pages
  3. Open your status page. The URL will look like http://your-host:3001/status/default
  4. The slug is the last part: default in that example

Enter the slug in Settings. CtrlFreak uses the public status page API, so no login credentials are needed for Uptime Kuma.

Dell iDRAC
How do I connect to iDRAC?

CtrlFreak connects to iDRAC8 via the Redfish API using Basic authentication. Enter the iDRAC IP address, your username, and password in Settings.

iDRAC8 requires TLS 1.2. Before connecting, go to your iDRAC web UI: iDRAC Settings > Services > Web Server and set TLS Protocol to TLS 1.2 and Higher. Without this the connection will fail.
Why does my iDRAC connection keep failing?

The most common cause is the TLS protocol setting. iDRAC8 defaults to TLS 1.0 in some firmware versions and the app requires TLS 1.2.

Fix: iDRAC web UI > iDRAC Settings > Services > Web Server > TLS Protocol: TLS 1.2 and Higher.

Other things to check:

  • Confirm the IP is reachable from your phone (same network or via VPN)
  • Confirm the username and password are correct for the iDRAC interface specifically (not the host OS)
  • iDRAC9 is not tested. Redfish endpoints may differ.
Pi-hole
How do I get my Pi-hole API token?
  1. Log into your Pi-hole web UI
  2. Go to Settings > API / Web interface
  3. Your API token is shown under API Token
  4. Copy the token and paste it into CtrlFreak Settings

The host should be the URL to your Pi-hole admin panel, e.g. http://192.168.1.100

Remote Access
How do I use CtrlFreak when I'm away from home?

Install Tailscale on your phone and on any always-on Linux box on your home network. Once both devices are signed into the same tailnet, CtrlFreak reaches your servers via MagicDNS hostnames (for example pve1.tail-abc123.ts.net) from anywhere with internet. No port forwards, no DDNS, no Cloudflare tunnel needed.

CtrlFreak does not bundle a VPN client. Tailscale runs as a separate app on your phone, governed by Tailscale's own privacy policy.

My iDRAC or iLO can't run a VPN client. How do I reach it remotely?

Set up a Tailscale subnet router on the same VLAN. Five-minute recipe on any always-on Linux box (Pi, NUC, existing VM):

sudo sysctl -w net.ipv4.ip_forward=1
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-tailscale.conf
sudo tailscale up --advertise-routes=192.168.1.0/24

Then go to the Tailscale admin console and approve the advertised route. Your out-of-band management controllers are now reachable from any device on your tailnet.

Should I enter Tailscale IPs (100.x) or MagicDNS hostnames in CtrlFreak?

MagicDNS hostnames. Tailnet IPs are in the CGNAT range (100.64.0.0/10), which iOS App Transport Security does not auto-allow without per-host configuration. MagicDNS hostnames (pve1.tail-abc123.ts.net) work out of the box and survive tailnet IP changes.

Local Network Discovery
Why does CtrlFreak ask for Local Network permission?

To scan your LAN via mDNS / Bonjour for servers advertising standard services. Found servers can be added with one tap instead of typing IP addresses by hand. The app works fully without granting this permission; manual entry is always available in each integration's settings screen.

Is anything sent off-device when CtrlFreak scans my network?

No. mDNS is broadcast-only on your LAN. Scan results never leave the device. GOW Studios has no access to your network topology.

Cloudflare Access
How do I use CtrlFreak with Cloudflare Access in front of my services?

Each integration's settings screen has an Advanced section with two optional fields: Cloudflare Access Client-Id and Client-Secret. Paste your service token credentials there, and CtrlFreak will send them as CF-Access-Client-Id and CF-Access-Client-Secret headers on every request to that host.

Leave both fields blank if you're not using Cloudflare Access; the integration will use only the credentials you provide for the service itself.

To generate a service token: Cloudflare Zero Trust dashboard > Access > Service Auth > Service Tokens > Create Service Token. Copy the Client-Id and Client-Secret immediately; the secret is shown only once.

Tailscale
How do I get a Tailscale API key?
  1. Go to the Tailscale admin console at login.tailscale.com/admin
  2. Go to Settings > Keys
  3. Click Generate access token
  4. Give it a description and set an expiry
  5. Copy the token (starts with tskey-api-)
Where do I find my tailnet name?

Your tailnet name is visible in the Tailscale admin console URL and on the DNS settings page.

It is either your organization name (e.g. example.com) or the email domain you signed up with (e.g. gmail.com becomes youremail.gmail.com.beta.tailscale.net).

The easiest way: go to admin console > DNS and your tailnet name is shown at the top under Tailnet name.

Tailscale is the only integration that contacts an external cloud API (api.tailscale.com). All other integrations connect directly to your local servers.
Unraid
How do I get an Unraid API key?
  1. Log into your Unraid web UI
  2. Go to Settings > Management Access
  3. Under API Keys, click Add
  4. Give it a name and copy the key

The host should be your Unraid server IP or hostname, e.g. http://192.168.1.52

The Unraid API requires the Unraid API plugin or Unraid 6.12+ which includes the API natively.
Synology DSM
How do I connect to Synology DSM?

CtrlFreak connects to Synology DSM using your admin username and password. The app authenticates via the Synology WebAPI and caches the session for 20 minutes per device.

The host should include the DSM port. Synology defaults to port 5000 for HTTP and port 5001 for HTTPS, e.g. http://192.168.1.54:5000 or https://192.168.1.54:5001

You can use your admin account or create a separate user with limited permissions. For read-only access, the user needs the Monitoring and Storage Manager read permissions.
What does the Synology integration show?

The Synology screen shows:

  • System info: model, DSM version, serial number, uptime, and CPU core count
  • Resource utilization: CPU percentage, memory usage, and network throughput (download and upload)
  • Volumes: usage bars, free/total space, and health status for each volume
  • Drives: per-disk status, model, temperature, and SMART health status
Can I add multiple Synology devices?

Yes. You can add multiple Synology servers in Settings. The screen shows a tab picker at the top when more than one device is configured.

Nginx Proxy Manager
How do I connect to Nginx Proxy Manager?

CtrlFreak connects using your NPM admin email and password. It authenticates via the NPM API and caches the session token for 23 hours.

The host should be your NPM instance including the port, e.g. http://192.168.1.73:81

NPM defaults to port 81 for the admin interface.

Proxmox Backup Server
How do I connect to Proxmox Backup Server?

CtrlFreak uses API token authentication. In the PBS web UI, go to Configuration > Access > API Tokens and create a token for a user that has Datastore.Audit permissions.

The token format is: user@realm!tokenname=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

The host should include the port, e.g. https://192.168.1.38:8007

Dockge
How do I connect to Dockge?

CtrlFreak connects using your Dockge admin username and password. Dockge defaults to port 5001, so the host should be: http://192.168.1.73:5001

The app authenticates and caches the session token automatically. If the token expires, it re-authenticates on the next request.

Beszel
How do I connect to Beszel?

CtrlFreak connects to Beszel using your account email address and password. Beszel defaults to port 8090, so the host should be: http://192.168.1.73:8090

The app uses the PocketBase authentication API that Beszel is built on.

HP iLO
How do I connect to HP iLO?

CtrlFreak connects to HP iLO 4 and iLO 5 via the Redfish API using Basic authentication. Enter the iLO IP address, your username (default is Administrator), and password in Settings.

The host should be the iLO management IP, e.g. https://192.168.1.51

AdGuard Home
How do I connect to AdGuard Home?

CtrlFreak connects using your AdGuard Home admin username and password. The default port is 3000, so the host should be: http://192.168.1.53:3000

If you have changed AdGuard's port in its settings, use that port instead.

OPNsense
How do I create an OPNsense API key?
  1. Log into your OPNsense web UI
  2. Go to System > Access > Users
  3. Edit your user and scroll to API keys
  4. Click + to generate a new key/secret pair
  5. Copy both the key and the secret before closing the dialog

The host should be your OPNsense LAN IP, e.g. https://192.168.1.1

OPNsense uses a self-signed certificate by default. CtrlFreak accepts self-signed certs, so HTTPS will work without additional setup.
TrueNAS
How do I create a TrueNAS API key?

In TrueNAS SCALE: go to Credentials > API Keys and click Add. Give it a name and copy the key.

In TrueNAS CORE: go to Accounts > Users, edit your admin user, and look for API Keys at the bottom of the page.

The host should be your NAS IP, e.g. http://192.168.1.52

CtrlFreak works with both TrueNAS SCALE and TrueNAS CORE.
SSL and Certificates
Does CtrlFreak support self-signed certificates?

Yes, with explicit per-host trust and SHA-256 fingerprint pinning. When you connect to a server with a self-signed certificate (typical for Proxmox, iDRAC, TrueNAS, and many homelab defaults), the app displays a one-time trust prompt showing the certificate's SHA-256 fingerprint.

Verify the fingerprint matches your server by running this on the server:

openssl x509 -noout -fingerprint -sha256 -in /etc/your-cert.pem

If the value matches, tap Trust. The fingerprint is pinned for that host in the device keychain. Subsequent connections compare the certificate against the pin. If the certificate later changes, the app blocks the connection and surfaces a re-trust prompt. This is the same security model SSH uses for host keys.

Connections are TLS-encrypted in both directions. The certificate fingerprint is a separate authentication layer on top of TLS, not a replacement for it.
I rotated my certificate and CtrlFreak is warning me. Is that normal?

Yes. The app detected a fingerprint change. If you rotated the cert intentionally (manual rotation, Let's Encrypt renewal, internal CA reissue), tap Re-trust and verify the new fingerprint matches your server using the openssl command above.

If you didn't rotate the cert, investigate before tapping Trust. A fingerprint change you didn't expect can indicate a man-in-the-middle attack on your network.

Can I disable certificate verification globally for all servers?

No, and we won't add that. Per-host opt-in via the verifySSL toggle in each integration's settings screen is the safe path. Each opted-in host gets its own fingerprint pin, so a future MITM against any single host is still detected even if you've opted multiple hosts out of strict TLS validation.

What does the SSL Health screen do?

The SSL Health screen (found in the Network Tools section) checks whether each of your configured servers is reachable over HTTPS. It shows a green dot and latency for hosts that respond, and a red dot for hosts that are unreachable or timing out.

It does not validate certificate chains. It only checks reachability.

My server uses HTTP, not HTTPS. Will CtrlFreak work?

Yes. You can enter HTTP addresses for any integration. There is no requirement to use HTTPS, though it is recommended where possible.

iDRAC is an exception: it only supports HTTPS connections via the Redfish API.