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.raketask 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
- Encryption Configuration:
config/initializers/active_record_encryption.rbadjusts key derivation strategies based on whether the instance is self‑hosted or managed.
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.rbsetsconfig.app_modeto"self_hosted"ifSELF_HOSTEDorSELF_HOSTING_ENABLEDequals"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=truein 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →