---
layout: article
title: Rails
description: Use Ruby on Rails and ActiveRecord with an Appwrite native MySQL database. Configure database.yml, run migrations against the direct database port, and size the ActiveRecord pool.
---

A native MySQL database is a standard MySQL engine, so [Ruby on Rails](https://rubyonrails.org/) works against it through ActiveRecord with no Appwrite-specific configuration. Point `config/database.yml` at the connection details from the [Connections](/docs/products/databases/mysql/connections) page and use ActiveRecord, migrations, and the rest of the Rails toolchain exactly as you would against any MySQL server.

**Before you start**

You'll need a native MySQL database in a `ready` state and its credentials. Call `mysql.get()` from the Appwrite API to read `hostname`, `connectionUser`, `connectionPassword`, and `connectionString`. The primary user is `admin`, and Appwrite generates the database name for each database.

# Install the database driver

ActiveRecord talks to MySQL through the `mysql2` driver gem. Add it to your `Gemfile`:

```ruby
# Gemfile
gem "mysql2", "~> 0.5"
```

The `mysql2` gem builds against MySQL client libraries, so the MySQL or MariaDB client headers must be available at install time.

Then install:

```bash
bundle install
```

# Set the connection string

Fetch the connection string with the [API](/docs/products/databases/mysql/connections#credentials). Keep it in an environment variable and do not commit it:

```env
DATABASE_URL="mysql://admin:<password>@db-<hash>.<region>.appwrite.center:3306/<database>"
DB_SSL_MODE="required"
```

Rails maps the `mysql://` URL scheme to the `mysql2` adapter by default. Set `ssl_mode` in `database.yml` so mysql2 requires TLS on Appwrite Cloud. For certificate verification (`verify_identity`) or mTLS, see the [Network](/docs/products/databases/mysql/network-security) page.

# Configure database.yml

Rails reads `DATABASE_URL` automatically. The simplest configuration points the `url` at the environment variable and lets ActiveRecord parse the host, port, database, and credentials out of it:

```yaml
# config/database.yml
production:
  adapter: mysql2
  url: <%= ENV["DATABASE_URL"] %>
  ssl_mode: <%= ENV.fetch("DB_SSL_MODE", "required") %>
  max_connections: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
```

If you'd rather set the fields explicitly, the discrete keys map one-to-one to the values from the [Connections](/docs/products/databases/mysql/connections#credentials) response. Use ERB to read each value from the environment so no secret lands in source control:

```yaml
# config/database.yml
production:
  adapter: mysql2
  encoding: utf8mb4
  host: <%= ENV["DB_HOST"] %>       # db-<hash>.<region>.appwrite.center
  port: <%= ENV.fetch("DB_PORT", 3306) %>
  database: <%= ENV["DB_NAME"] %>   # <database>
  username: <%= ENV.fetch("DB_USER", "admin") %>
  password: <%= ENV["DB_PASSWORD"] %>
  ssl_mode: <%= ENV.fetch("DB_SSL_MODE", "required") %>
  max_connections: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
```

When both `DATABASE_URL` and explicit keys are present, Rails merges them. `ssl_mode` and `max_connections` can still be set in `database.yml`, so a `url`-based config can keep secrets in the environment and tune the connection pool in YAML.

# Size the connection pool

ActiveRecord manages a per-process connection pool. In current Rails apps, `max_connections:` caps how many backend connections a single Rails process holds. Older apps may use the `pool:` name for the same setting. The value defaults to `5` and must be large enough for every thread that checks out a connection, your Puma worker threads plus any background job threads in the same process.

```yaml
production:
  adapter: mysql2
  url: <%= ENV["DATABASE_URL"] %>
  ssl_mode: <%= ENV.fetch("DB_SSL_MODE", "required") %>
  max_connections: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
```

Tying `max_connections` to `RAILS_MAX_THREADS` keeps it aligned with Puma's thread count. Each Puma **worker** is a separate process with its own pool, so the backend connection count is roughly `max_connections × workers × server instances`. Keep that product within your specification's `maxConnections`, see [specifications](/docs/products/databases/mysql#specifications).

# Run migrations

Generate and apply migrations the usual way:

```bash
bin/rails db:migrate
```

Migrations issue DDL and need a session-level connection, so run them against the direct MySQL port `3306`, not the transaction-mode pooler. Point `DATABASE_URL` (or a separate migration URL) at the direct port when you run `db:migrate`. The primary `admin` user owns the database and can run schema changes.

# Use ActiveRecord

Once `database.yml` is configured, models work with no further setup. Define a migration and model, then query through ActiveRecord:

```ruby
# db/migrate/20240101000000_create_rails_users.rb
class CreateRailsUsers < ActiveRecord::Migration[8.1]
  def change
    create_table :rails_users do |t|
      t.string :email, null: false
      t.timestamps
    end
    add_index :rails_users, :email, unique: true
  end
end
```

Define the matching model:

```ruby
# app/models/rails_user.rb
class RailsUser < ApplicationRecord
  validates :email, presence: true, uniqueness: true
end
```

Create and query records as usual:

```ruby
RailsUser.create!(email: 'ada@example.com')

recent = RailsUser.order(created_at: :desc).limit(10)
```

ActiveRecord opens connections lazily and reuses them from the pool, so a long-running Puma server keeps a small, stable set of backend connections rather than opening one per request.

# Pooling for a long-running server

A Rails app under Puma is a long-running process: it holds an ActiveRecord pool for its lifetime. That pairs naturally with the direct MySQL port `3306`, sized so `max_connections × workers` stays within your connection budget. This is the recommended setup for a persistent server.

If you instead route through the [connection pooler](/docs/products/databases/mysql/connection-pooling) to absorb spikes or many app instances, prefer **session mode**, which keeps a backend connection for the whole client session and behaves like a direct connection to ActiveRecord. The pooler defaults to **transaction mode**, which hands out a different backend connection per transaction. If your Rails configuration enables server-side prepared statements, disable them for transaction-mode pooling or use session mode:

```yaml
production:
  adapter: mysql2
  url: <%= ENV["DATABASE_URL"] %>       # pooler host, port 6033
  ssl_mode: <%= ENV.fetch("DB_SSL_MODE", "required") %>
  max_connections: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
  prepared_statements: false
```

See the [pooler](/docs/products/databases/mysql/connection-pooling#modes) page for the mode trade-offs. Whichever runtime connection you choose, always run `bin/rails db:migrate` against the direct MySQL port.

# Use a branch for previews and CI

[Branches](/docs/products/databases/mysql/branches) are instant, isolated copies of a database with their own hostname and connection string. They're ideal for running migrations against throwaway data in a pull-request preview or an integration-test job:

1. Create a branch from the API and read its `connectionString`.
2. Export it as `DATABASE_URL` for the job.
3. Run `bin/rails db:migrate` and your test suite against the branch.
4. Delete the branch when the job finishes.

A branch has no pooler and exposes the MySQL port directly, which gives migrations the session-level connection they need. Because a branch starts from a storage snapshot, the schema and data match the source database at branch time, so migrations run against realistic data without touching production.

# Related

- [Connections](/docs/products/databases/mysql/connections): Retrieve credentials and rotate the primary password.
- [Connection pooler](/docs/products/databases/mysql/connection-pooling): Pool modes, ports, and read/write splitting for high-concurrency workloads.
- [Branches](/docs/products/databases/mysql/branches): Ephemeral database copies for preview environments and CI.
- [Network](/docs/products/databases/mysql/network-security): TLS modes, certificate verification, mTLS, and IP allowlists.

For ActiveRecord and migration details beyond this guide, see the [Rails configuration guide](https://guides.rubyonrails.org/configuring.html).
