How to Monitor Rails App CPU Memory and Disk on Hatchbox with AppSignal 2025

Prerequisites and What You'll Need Before Starting

Before diving in, make sure you have everything lined up. Rushing the setup without the right accounts and versions in place leads to silent failures that are annoying to debug.

  • [ ] Hatchbox account with at least one server provisioned (Hobby plan or higher) and a Rails app successfully deployed
  • [ ] AppSignal account — sign up at appsignal.com; free trial covers this setup fully
  • [ ] Ruby 3.0 or higher — AppSignal gem ~> 3.x requires Ruby 2.7+ but 3.0+ is strongly recommended for 2025 production deployments
  • [ ] Rails 6.1 or higher on your app
  • [ ] Local Gemfile access with ability to bundle install and push to your connected Git repo
  • [ ] SSH access to your Hatchbox server (the deploy user key, available in Hatchbox dashboard under Servers → SSH Keys)

Compatibility Matrix

| Component | Minimum Version | Recommended Version | |---|---|---| | Ruby | 2.7 | 3.2+ | | Rails | 6.1 | 7.1+ | | appsignal gem | 3.0.0 | 3.7.x (latest stable) | | Hatchbox Plan | Hobby | Growth (for multiple servers) |

Note: AppSignal's host metrics agent is bundled with the Ruby gem starting from version 3.x. You do not need to install a separate system daemon — the agent spawns automatically when your Rails process starts.

You'll also want your AppSignal Push API Key handy. Find it under AppSignal → App Settings → Push & Deploy → Push API Key.

Estimated time: 25 minutes


Understanding How AppSignal Host Monitoring Works on Hatchbox

Before touching any config, it's worth understanding what's actually happening under the hood — this will save you from misdiagnosing issues later.

What AppSignal Host Monitoring Tracks

When host metrics are enabled, AppSignal collects the following at 60-second intervals:

  • CPU usage — broken down by user, system, and idle percentages
  • Memory — used, free, and cached RAM in bytes
  • Disk I/O — read/write throughput per device
  • Disk usage — percentage used per mount point
  • Load average — 1-minute, 5-minute, and 15-minute averages
  • Network — bytes transmitted and received per interface

How Hatchbox Provisions Servers

Hatchbox abstracts VPS providers — it provisions DigitalOcean Droplets or Hetzner Cloud instances on your behalf, then runs an opinionated deployment stack on top (Nginx, Puma, PostgreSQL, Redis). Your app lives at /home/deploy/apps/<appname>/current/. This matters for monitoring because:

  1. You're running on a standard Linux VPS — no containers, no orchestration layer
  2. The deploy user runs your Puma process
  3. AppSignal's agent can read /proc metrics directly because it has full OS-level access

Architecture: The AppSignal Agent vs. the Ruby Gem

Think of it as two components sharing a process:

┌─────────────────────────────────────────────┐
│  Puma Worker Process (your Rails app)        │
│                                             │
│  ┌─────────────────┐  ┌──────────────────┐  │
│  │  AppSignal Ruby  │  │  Host Metrics    │  │
│  │  APM (gem)       │  │  Agent (bundled) │  │
│  │  - Errors        │  │  - CPU           │  │
│  │  - Performance   │  │  - Memory        │  │
│  │  - Queries       │  │  - Disk          │  │
│  └────────┬─────────┘  └──────┬───────────┘  │
│           └──────────┬────────┘              │
│                      ▼                       │
│            AppSignal Push API                │
│            (push.appsignal.com)              │
└─────────────────────────────────────────────┘

The host metrics agent is a lightweight Go binary embedded in the gem. It forks from the main Ruby process at startup and reads OS-level metrics via /proc. Because Hatchbox doesn't use containers (unlike Heroku), this works without any special configuration — the agent sees real hardware metrics, not cgroup-limited views.


Step 1: Add the AppSignal Gem to Your Rails App

The gem install is straightforward, but the placement in your Gemfile matters — a common mistake is scoping it to :development only, which means it never loads in production.

Add to Gemfile

# Gemfile

source 'https://rubygems.org'

# ... other gems ...

# AppSignal: include in default group so it loads in all environments,
# or explicitly list :production and :staging if you prefer.
gem 'appsignal', '~> 3.7'

Then install:

bundle install

Run the Install Wizard

AppSignal ships an interactive installer that generates your config file:

bundle exec appsignal install YOUR_PUSH_API_KEY

The wizard will ask for your app name and which frameworks you're using. For a standard Rails app, select Rails when prompted. This generates config/appsignal.yml.

Review the Generated appsignal.yml

Here's a complete annotated version of what your config should look like after the wizard runs (with host metrics explicitly enabled):

# config/appsignal.yml

default: &defaults
  # Your app name as it appears in the AppSignal dashboard.
  # Use a consistent name — changing it creates a new app entry.
  name: "MyRailsApp"

  # Push API key — NEVER hardcode this. Use an environment variable.
  # Set this in Hatchbox's environment variables panel.
  push_api_key: "<%= ENV.fetch('APPSIGNAL_PUSH_API_KEY') %>"

  # Enable the host metrics agent. This is the critical flag.
  # Without this, AppSignal only tracks app-level APM, not OS metrics.
  enable_host_metrics: true

  # Override the hostname reported to AppSignal.
  # Match this to your Hatchbox server label for easy cross-referencing.
  hostname: "<%= ENV.fetch('APPSIGNAL_HOSTNAME', Socket.gethostname) %>"

  # Reduce log noise in production. Use 'debug' temporarily if troubleshooting.
  log_level: "warn"

  # Instrument standard Rails components automatically.
  instrument_net_http: true
  instrument_redis: true
  instrument_sequel: false  # Set true if using Sequel instead of ActiveRecord

development:
  <<: *defaults
  active: true
  log_level: "debug"  # More verbose locally for easier debugging

staging:
  <<: *defaults
  active: true
  # Optionally use a separate AppSignal app for staging:
  # push_api_key: "<%= ENV.fetch('APPSIGNAL_STAGING_PUSH_API_KEY') %>"

production:
  <<: *defaults
  active: true
  # Production is explicit — never rely on defaults silently enabling prod reporting

Note: The <%= ... %> ERB syntax works because AppSignal processes appsignal.yml through ERB before parsing. This means you can safely use ENV.fetch without any additional setup.


Step 2: Enable Host Metrics in Your AppSignal Configuration

The enable_host_metrics: true flag in the default block is a good start, but there are three additional settings that make Hatchbox-specific monitoring significantly more useful.

The Full Production Block

# config/appsignal.yml — production section (replace or extend your existing config)

production:
  name: "MyRailsApp"
  push_api_key: "<%= ENV.fetch('APPSIGNAL_PUSH_API_KEY') %>"

  # This flag is mandatory. If omitted, host metrics are silently disabled.
  enable_host_metrics: true

  # Use the Hatchbox server label as your hostname.
  # In Hatchbox, each server gets a label like "web-01" or "prod-worker".
  # Set APPSIGNAL_HOSTNAME in Hatchbox's environment variables panel to match.
  hostname: "<%= ENV.fetch('APPSIGNAL_HOSTNAME', Socket.gethostname) %>"

  # Suppress info/debug log lines — only warnings and errors in production.
  log_level: "warn"

  active: true

Setting Environment Variables in Hatchbox

Hatchbox has a built-in environment variables panel (App → Environment Variables). Set these three variables:

# These are the equivalent shell exports — set them in Hatchbox's UI,
# not in a .env file that gets committed to your repo.

export APPSIGNAL_PUSH_API_KEY="your-push-api-key-from-appsignal-dashboard"
export APPSIGNAL_HOSTNAME="web-01"  # Match exactly to your Hatchbox server label
export RAILS_ENV="production"

In Hatchbox's UI: navigate to your App → Environment Variables → Add Variable. Add each key-value pair there. Hatchbox injects these into the deploy environment and into the running Puma process.

Note: Never store APPSIGNAL_PUSH_API_KEY in config/appsignal.yml as a plain string or in your Git repo. The ERB ENV.fetch approach means the key is injected at runtime from Hatchbox's secrets management, keeping it out of version control entirely.

Why Hostname Matching Matters

If you have a web server and a Sidekiq worker server on Hatchbox, both will report to the same AppSignal app. Without explicit hostnames, they'll show up as whatever the OS hostname is (usually a DigitalOcean-generated string like ubuntu-s-2vcpu-4gb-nyc1-01). Setting APPSIGNAL_HOSTNAME to something readable like web-01 and worker-01 makes the Host Monitoring dashboard immediately legible.


Step 3: Deploy to Hatchbox and Verify the Agent Is Running

With the gem added and config in place, a deploy triggers AppSignal to start. Verification takes two minutes and confirms everything is wired up before you invest time in alert configuration.

Trigger the Deploy

Push your changes to the branch Hatchbox is tracking (usually main):

git add Gemfile Gemfile.lock config/appsignal.yml
git commit -m "Add AppSignal with host metrics enabled"
git push origin main

Hatchbox will pick up the push automatically if you've enabled auto-deploy, or you can click Deploy in the Hatchbox dashboard.

SSH In and Check the Agent Logs

Once the deploy finishes, SSH into your server and tail the AppSignal log:

# SSH into your Hatchbox server
# Replace YOUR_SERVER_IP with the IP shown in Hatchbox → Servers
ssh deploy@YOUR_SERVER_IP

# Tail the AppSignal log — replace 'myapp' with your actual app name
tail -f /home/deploy/apps/myapp/current/log/appsignal.log

A successful agent startup looks like this:

[2025-01-15 10:23:41 UTC][appsignal][INFO] Starting AppSignal agent
[2025-01-15 10:23:41 UTC][appsignal][INFO] AppSignal agent 3.7.2 started
[2025-01-15 10:23:41 UTC][appsignal][INFO] Starting host metrics collection
[2025-01-15 10:23:41 UTC][appsignal][INFO] Hostname: web-01
[2025-01-15 10:23:42 UTC][appsignal][INFO] Transmitting metrics to push.appsignal.com

A failed startup (missing API key or wrong environment) looks like:

[2025-01-15 10:23:41 UTC][appsignal][ERROR] Push API key not set or invalid
[2025-01-15 10:23:41 UTC][appsignal][ERROR] AppSignal not active for environment: production
[2025-01-15 10:23:41 UTC][appsignal][WARN] Host metrics collection disabled

Confirm in the AppSignal Dashboard

In AppSignal, navigate to Hosts in the left sidebar. Within 60 seconds of a successful agent start, your server should appear with its hostname label. Click into it to see the CPU, memory, disk, and load average graphs populating with live data.

If the host doesn't appear after 2 minutes, jump to the Common Issues section below before continuing.


Step 4: Set Up Alerts for CPU, Memory, and Disk Thresholds

Metrics without alerts are just pretty graphs. This step turns your monitoring into something that actually pages you when production is struggling.

Navigate to Anomaly Detection

In AppSignal: go to AlertsAnomaly DetectionNew Trigger. You'll create separate triggers for CPU, memory, and disk.

Recommended Thresholds for Hatchbox Servers

For a typical Hatchbox server with 2-4GB RAM (DigitalOcean Basic Droplet or Hetzner CX21/CX31):

| Metric | Warning Threshold | Critical Threshold | Duration | |---|---|---|---| | CPU usage | 70% | 90% | 5 minutes | | Memory used | 75% of total | 90% of total | 5 minutes | | Disk usage | 80% of capacity | 90% of capacity | 1 minute | | Load average | 1.5× vCPU count | 2× vCPU count | 5 minutes |

Note: Load average thresholds are relative to vCPU count. A 2-vCPU server (DigitalOcean Basic 2GB) should trigger a warning at load 3.0 and critical at load 4.0. A 4-vCPU server shifts those to 6.0 and 8.0 respectively.

Creating a CPU Alert

In the New Trigger form:

  1. Metric: Select host.cpuusage
  2. Condition: above 80
  3. Warmup period: 5 minutes (prevents noise from brief spikes)
  4. Hostname filter: Select your specific server (e.g., web-01) or leave blank for all hosts
  5. Severity: Warning for 70%, Critical for 90% (create two separate triggers)

Creating a Disk Alert

For disk, select host.diskused_percentage, set threshold to 85, duration to 1 minute. Disk alerts should fire faster than CPU alerts because a full disk causes immediate Rails failures (log writes fail, Active Storage uploads fail, database writes can fail if PostgreSQL data directory fills up).

Routing Alerts to Slack or Email

In AppSignal → NotificationsNotifiers, add your Slack webhook URL or email address. When creating each trigger, select the notifier in the Notify via dropdown. You can route warning-level alerts to a Slack channel and critical alerts to PagerDuty or email to ensure the right urgency reaches the right person.


Common Issues and Fixes When Setting Up Monitoring on Hatchbox

Error: Host metrics not appearing in AppSignal dashboard after deploy

Cause: enable_host_metrics is missing or set to false in the environment block that's actually loaded.

Fix: Confirm the flag exists in your production block (or default block with YAML anchors). Also verify that RAILS_ENV=production is set in Hatchbox — if it's missing, AppSignal loads the development config which may have the flag disabled.

# Verify this exact line exists in your production: block
production:
  <<: *defaults
  enable_host_metrics: true  # Must be explicit — do not rely on default: block alone
  active: true

Error: Wrong hostname shown in AppSignal (shows DigitalOcean-generated hostname)

Cause: APPSIGNAL_HOSTNAME environment variable is not set in Hatchbox, so Socket.gethostname falls back to the OS hostname assigned by DigitalOcean or Hetzner at provisioning time.

Fix: In Hatchbox → App → Environment Variables, add APPSIGNAL_HOSTNAME with a human-readable value like web-01. Redeploy. The new hostname appears immediately on the next metric transmission (within 60 seconds).

Error: AppSignal gem not loading in production — uninitialized constant Appsignal

Cause: The gem is scoped to the wrong Bundler group — typically :development or :test only — so it's not bundled in the production deployment.

Fix: Ensure appsignal is in the default group or explicitly in :production:

# WRONG — appsignal only loads in development, not production
group :development do
  gem 'appsignal', '~> 3.7'
end

# CORRECT — appsignal loads in all environments
gem 'appsignal', '~> 3.7'

# ALSO CORRECT — explicit multi-environment group
group :production, :staging do
  gem 'appsignal', '~> 3.7'
end

# If you want it everywhere except test (common pattern):
gem 'appsignal', '~> 3.7', require: false  # Then require manually in config
# OR simply leave it in the default group

After fixing the Gemfile, run bundle install locally, commit Gemfile.lock, and push.

Error: Push API key not set or invalid in appsignal.log

Cause: Environment mismatch — either APPSIGNAL_PUSH_API_KEY isn't set in Hatchbox's environment variables panel, or the key belongs to a different AppSignal app than what RAILS_ENV is pointing to. AppSignal push API keys are per-app and per-environment.

Fix: In AppSignal dashboard, go to App Settings → Push & Deploy → verify the key matches the environment (Production vs. Staging). Then in Hatchbox, confirm APPSIGNAL_PUSH_API_KEY is set to exactly that key with no trailing whitespace. You can verify the env var is available on the server:

ssh deploy@YOUR_SERVER_IP
cd /home/deploy/apps/myapp/current
bundle exec rails runner "puts ENV['APPSIGNAL_PUSH_API_KEY'].inspect"
# Should print a non-nil string

FAQ: Rails Server Monitoring on Hatchbox with AppSignal

Q: Does AppSignal host monitoring work on all Hatchbox server sizes?

Yes. AppSignal's host metrics agent is lightweight — it uses under 20MB of RAM and negligible CPU (under 0.5% on a single vCPU). It runs on any server size Hatchbox provisions, including the smallest DigitalOcean Basic Droplets (1GB RAM, 1 vCPU). The agent reads from /proc which is available on all Linux distributions Hatchbox uses (Ubuntu 22.04 LTS as of 2025). The only practical limit is that very small servers (512MB RAM) should use AppSignal gem 3.5+ which includes memory footprint optimizations.

Q: Can I monitor multiple Hatchbox servers for one Rails app in AppSignal?

Yes — this is one of AppSignal's strengths for Hatchbox deployments. All servers configured with the same push_api_key and app name report to the same AppSignal app. In the Hosts dashboard, each server appears as a separate entry identified by its hostname value. This is why setting APPSIGNAL_HOSTNAME to distinct values like web-01, web-02, and worker-01 is important. You can filter metric graphs by hostname, set per-host alert triggers, and see at a glance which server is under load. For Sidekiq workers on a separate Hatchbox server, set APPSIGNAL_HOSTNAME=worker-01 in that app's environment variables.

Q: How does AppSignal host monitoring compare to Hatchbox's built-in server stats?

Hatchbox provides basic CPU and RAM graphs in its dashboard, which are useful for a quick sanity check immediately after deployment. However, Hatchbox's built-in stats have no alerting, no anomaly detection, and no correlation with application-level data. You can't answer "was the memory spike at 3am caused by this specific slow query?" with Hatchbox's dashboard alone. AppSignal links host metrics to error traces and performance samples from the same time window, so you can correlate a CPU spike with a specific action or background job. It also retains historical data for 30+ days depending on your plan, while Hatchbox's graphs show only a short rolling window.