How Self‑Hosted vs Managed App Mode Configuration Works in Maybe Finance

Maybe Finance uses environment variables in config/application.rb to set Rails.application.config.app_mode to either "self_hosted" or "managed", which then controls rate limits, database migrations, and feature availability throughout the application.

The self‑hosted vs managed app mode configuration is a fundamental architectural decision in Maybe that determines whether the application runs as a multi‑tenant SaaS platform (managed) or as a single‑tenant instance deployed by individual users (self‑hosted). This configuration is evaluated once during Rails initialization and stored as a global application setting.

How App Mode Is Determined During Initialization

Environment Variable Detection in config/application.rb

The mode selection logic resides in config/application.rb, where the application inspects two specific environment variables during the boot process:


# config/application.rb

config.app_mode = (
  ENV["SELF_HOSTED"] == "true" ||
  ENV["SELF_HOSTING_ENABLED"] == "true"
) ? "self_hosted" : "managed"

If either SELF_HOSTED or SELF_HOSTING_ENABLED equals the string "true", the application configures itself for self‑hosted operation. Otherwise, it defaults to managed mode. This evaluation occurs before most initializers run, ensuring downstream components can reference the mode immediately.

ActiveSupport::StringInquirer for Predicate Methods

To enable readable conditional checks throughout the codebase, Maybe wraps the mode string in an ActiveSupport::StringInquirer via the .inquiry method (applied automatically by Rails string configuration values). This creates boolean predicate methods:

Rails.application.config.app_mode.self_hosted?   #=> true in self‑hosted deployments
Rails.application.config.app_mode.managed?       #=> true in managed deployments

These predicates are used extensively in initializers, models, and controllers to branch logic without hard‑coding string comparisons.

Operational Differences Between Self‑Hosted and Managed Modes

Rate Limiting Configuration

The config/initializers/rack_attack.rb file uses the app mode to adjust request throttling thresholds. Self‑hosted instances receive significantly higher limits because they typically run behind private firewalls with fewer users:


# config/initializers/rack_attack.rb

self_hosted = Rails.application.config.app_mode.self_hosted?

throttle("api/requests", limit: self_hosted ? 10_000 : 100, period: 1.hour) { |req| req.ip }
throttle("api/ip",      limit: self_hosted ? 20_000 : 200, period: 1.hour) { |req| req.ip }

Managed mode uses conservative limits (100 requests per hour for API endpoints) to prevent abuse in multi‑tenant environments, while self‑hosted mode allows 10,000–20,000 requests per hour to accommodate data imports and internal network usage.

Database Migration Behavior

Certain database migrations are conditional based on the deployment mode. For example, subscription‑related tables required for billing in the managed SaaS are skipped during self‑hosted deployments:


# db/migrate/20250502164951_create_subscriptions.rb

if Rails.application.config.app_mode.managed?
  # create subscription-related tables and columns

end

This keeps the self‑hosted database schema lean, excluding multi‑tenancy infrastructure that single‑tenant instances do not require.

Feature-Specific Logic

The codebase contains numerous feature toggles that reference the app mode:

  • Email Confirmation Bypass: In app/models/user.rb, self‑hosted instances can skip email confirmation requirements based on additional settings, streamlining local deployments.
  • External API Key Handling: The lib/tasks/securities.rake task selects between a user‑provided Synth API key (self‑hosted) or a centralized service key (managed):

# lib/tasks/securities.rake

api_key = if Rails.application.config.app_mode.self_hosted?
            Setting.synth_api_key
          else
            ENV["SYNTH_API_KEY"]
          end

Configuring Self‑Hosted Mode in Production

To deploy Maybe in self‑hosted mode, set one of the required environment variables before the Rails server starts. In a Docker‑Compose deployment, add the variable to the service definition:


# docker-compose.yml

services:
  web:
    image: ghcr.io/maybe-finance/maybe:latest
    environment:
      - SELF_HOSTED=true
      # or: SELF_HOSTING_ENABLED=true

    ports:
      - "3000:3000"

After restarting the application, verify the configuration in a Rails console:

bin/rails console
Rails.application.config.app_mode.self_hosted?  # => true

Rails.application.config.app_mode.managed?      # => false

Summary

  • Initialization: config/application.rb sets config.app_mode to "self_hosted" if SELF_HOSTED or SELF_HOSTING_ENABLED equals "true", otherwise defaulting to "managed".
  • Query Interface: The mode is stored as an ActiveSupport::StringInquirer, enabling .self_hosted? and .managed? predicate methods throughout the codebase.
  • Behavioral Differences: Self‑hosted mode increases Rack Attack rate limits (10,000+ requests/hour), skips subscription‑related database migrations, and alters feature logic for email confirmation and external API keys.
  • Configuration: Set SELF_HOSTED=true in your environment (e.g., Docker‑Compose) and restart the application to activate self‑hosted mode.

Frequently Asked Questions

What environment variables trigger self-hosted mode in Maybe?

Maybe checks for two environment variables during startup: SELF_HOSTED or SELF_HOSTING_ENABLED. If either variable is set to the exact string "true", the application configures itself for self-hosted operation. Any other value (or absence of these variables) results in managed mode.

How do I verify which mode my Maybe instance is running?

Start a Rails console by running bin/rails console in your application directory. Then query the configuration object: Rails.application.config.app_mode.self_hosted? returns true for self-hosted deployments, while Rails.application.config.app_mode.managed? returns true for the SaaS version. You can also inspect the raw value with Rails.application.config.app_mode to see the string "self_hosted" or "managed".

Does self-hosted mode disable all subscription features?

Self-hosted mode excludes database tables and application logic specifically related to multi-tenant billing and subscription management, such as the subscriptions table migration found in db/migrate/20250502164951_create_subscriptions.rb. However, the core financial tracking features remain fully functional. Self-hosted users typically manage their own infrastructure costs rather than paying per-user SaaS fees, so the subscription logic is bypassed rather than disabled.

Can I switch from managed to self-hosted mode without data loss?

Switching modes requires changing the SELF_HOSTED or SELF_HOSTING_ENABLED environment variable and restarting the application. While the mode change itself does not delete data, the database schema may differ between modes—managed mode includes subscription-related tables that self-hosted mode skips. If you are migrating from managed to self-hosted, ensure your database migrations have run completely in managed mode before switching, or manually verify that your schema matches the self-hosted expectations. Always back up your database before changing operational modes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →