Ahoy: First-Party Analytics for Rails, Stored in Your Own Database
Simple, powerful, first-party analytics for Rails
At a glance
- What is it?
- Ahoy is a Rails gem that tracks visits and events in Ruby, JavaScript, and native apps and stores the data in the application's own database, giving teams full control over their analytics data without routing it through a third-party service.
- Who is it for?
- Ahoy is the right choice for Rails teams that need event and visit tracking without sending data to a third-party analytics vendor, and who already have the database capacity to store that data. It is not the right choice for teams that need a ready-made analytics dashboard: Ahoy stores data but does not visualise it.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why First-Party Analytics in the Rails Database
Ahoy positions itself as first-party analytics. The distinction matters for two reasons. First, data stays in the application's own database, under the operator's control and subject to their data retention policies. Second, it does not require loading a third-party JavaScript snippet, which is a common source of GDPR compliance complexity and browser-side privacy blocking.
The gem is named ahoy_matey on RubyGems and is installed as any other Rails gem. It tracks two kinds of records: visits (each web session, enriched with traffic source, location, technology, and UTM parameters) and events (named actions with arbitrary property hashes). Both are stored in the application's existing database, using Active Record models in the Ahoy namespace.
The README notes the gem is "battle-tested at Instacart," an indication of production scale. It also lists two companion gems: Ahoy Email for email open and click tracking, and Field Test for A/B testing, suggesting a broader analytics ecosystem built around the same first-party, own-database principle.
What a Visit Record Contains
When a visitor loads a page, Ahoy creates one visit record. The README documents the fields it captures:
- Traffic source: referrer, referring domain, and landing page - Location: country, region, city, latitude, and longitude (requires geocoding setup) - Technology: browser, operating system, and device type - UTM parameters: source, medium, term, content, and campaign
The visit record is accessible in controllers through the current_visit method. Certain actions can be excluded from visit creation:
skip_before_action :track_ahoy_visitThis is documented as useful for API endpoints. For applications that are entirely API-based:
Ahoy.api_only = trueBots are excluded from tracking by default. To include them:
Ahoy.track_bots = trueCustom exclusion rules use a lambda:
Ahoy.exclude_method = lambda do |controller, request|
request.ip == "192.168.1.1"
endVisit duration defaults to four hours of inactivity before a new visit is created. Visitor token duration defaults to two years. Both are configurable:
Ahoy.visit_duration = 30.minutes
Ahoy.visitor_duration = 30.daysInstalling Ahoy and Tracking the First Event
Add the gem to the application's Gemfile:
gem "ahoy_matey"Then run the generator and migrate the database:
bundle install
rails generate ahoy:install
rails db:migrateRestarting the web server after migration is required. The README notes that opening a page in a browser after restart creates the first visit record. Track a named event from any controller:
ahoy.track "My first event", language: "Ruby"For JavaScript tracking, enable the API in the Ahoy initializer:
Ahoy.api = trueWith Importmap (the Rails default), add to the importmap configuration:
pin "ahoy", to: "ahoy.js"For Bun, esbuild, rollup.js, or Webpack:
bun add ahoy.jsIn either case, import ahoy and track from JavaScript:
ahoy.track("My second event", {language: "JavaScript"});Associating Visits with Active Record Models
One of Ahoy's practical features is associating visits with application models through the visitable declaration. To record which visit a user placed an order on:
class Order < ApplicationRecord
visitable :ahoy_visit
endThe migration to add the foreign key column:
class AddAhoyVisitToOrders < ActiveRecord::Migration[8.1]
def change
add_reference :orders, :ahoy_visit
end
endWith this in place, Ahoy automatically sets ahoy_visit_id when an order is created during a tracked visit. Queries then span both tables:
Order.joins(:ahoy_visit).group("referring_domain").count
Order.joins(:ahoy_visit).group("city").count
Order.joins(:ahoy_visit).group("device_type").countThis pattern connects business outcomes (orders, signups, purchases) to visit attributes (traffic source, geography, device) using standard Active Record joins, without building a separate analytics pipeline.
Ahoy also automatically attaches the current_user to each visit when Devise is used. For other authentication frameworks, calling ahoy.authenticate(user) at sign-in time links the user to the current visit even if the visit started before authentication.
GDPR Compliance Options and Limitations
The README includes a GDPR section and documents several compliance-relevant options. Ahoy provides the ability to disable cookies and configure anonymity sets for environments where individual tracking is restricted.
The primary GDPR-relevant feature is cookie control. By default, Ahoy uses cookies to maintain visitor and visit tokens. For sites that need to track across subdomains:
Ahoy.cookie_domain = :allCookie attributes can be set for SameSite and other properties:
Ahoy.cookie_options = {same_site: :lax}Since all data is in the application's own database, deletion of a user's records on request is a direct database operation, unlike third-party analytics where deletion depends on the vendor's API.
The README also documents server_side_visits control, which affects how bots interact with the tracking system. Setting Ahoy.server_side_visits = :when_needed defers creating a visit server-side until an event actually requires one. This prevents bots and users with cookies disabled from generating a new visit record on each page load without a corresponding event. Setting it to false disables server-side visit creation entirely and discards events that have no visit associated with them. Neither option is the default, so high-traffic applications with substantial bot traffic should evaluate these settings carefully at setup time before the site goes live.
The primary operational limitation of Ahoy is database write volume. Every visit and every tracked event is a database insert. A high-traffic application tracking page views will generate substantial write load. The README does not document batching or asynchronous write mechanisms, so the default behaviour is synchronous writes in the request cycle.
Mixpanel as the SaaS Alternative
Mixpanel is a well-known SaaS analytics platform used by Rails and non-Rails applications alike. The fundamental difference is data custody. Mixpanel receives event data from a JavaScript snippet or server-side SDK and stores it on Mixpanel's infrastructure. Ahoy stores all data in the application's own database. For organisations under data residency requirements or with strict data-sharing policies, Ahoy eliminates the third-party data transfer entirely.
Mixpanel provides a built-in analytics dashboard with funnel analysis, retention reports, and cohort analysis. Ahoy stores the raw records and provides no dashboard. Teams using Ahoy typically build their own reporting queries or integrate with a separate analytics front-end like Metabase or Redash pointed at the same database.
The practical choice depends on the team's priorities. Mixpanel requires no reporting development but requires data to leave the application. Ahoy requires building or integrating reporting but keeps data on the team's own infrastructure. For Rails applications that already run a PostgreSQL or MySQL database and want to avoid third-party analytics vendors, Ahoy is the simpler path; for teams that need rich analytics without custom development, Mixpanel serves the use case Ahoy deliberately leaves open.
Editorial conclusion
Ahoy is the right choice for Rails teams that need event and visit tracking without sending data to a third-party analytics vendor, and who already have the database capacity to store that data. It is not the right choice for teams that need a ready-made analytics dashboard: Ahoy stores data but does not visualise it. Before deploying, verify that your database can handle the insert volume your traffic generates, since every visit and event is a database write, and consider enabling Ahoy.server_side_visits = :when_needed to avoid recording bot traffic.
Frequently asked questions
Does Ahoy require a separate analytics database or server?
No. Ahoy stores visit and event records in the Rails application's existing database using Active Record. No separate analytics database, server, or external service is required. The rails generate ahoy:install command creates the necessary migrations.
Can Ahoy track events from JavaScript or mobile apps?
Yes. The README documents JavaScript tracking through Importmap, Bun, esbuild, rollup.js, Webpack, and Sprockets by enabling Ahoy.api = true in the initializer and importing ahoy.js. Native app tracking is handled through Ahoy iOS and Ahoy Android companion libraries, which are separate repositories.
How does Ahoy handle bot traffic?
Bots are excluded from tracking by default. The Ahoy.track_bots = true configuration includes them. Custom exclusion rules can be set via the Ahoy.exclude_method lambda, which receives the controller and request objects and returns true to exclude a given request.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ankane-ahoy)