# Configure Phone Sign-in & MFA

Set up phone sign-in SMS providers, OTP settings, and multi-factor authentication for self-hosted Supabase with Docker.

This guide covers the **server-side configuration** for phone sign-in and multi-factor authentication (MFA) on a self-hosted Supabase instance running with Docker Compose.

For client-side implementation, see [Phone sign-in](/docs/guides/auth/phone-login) and [Multi-Factor Authentication](/docs/guides/auth/auth-mfa).

## Before you begin

You need:

- A working self-hosted Supabase installation. See [Self-Hosting with Docker](/docs/guides/self-hosting/docker).
- An account with an SMS provider (e.g., Twilio)

Phone auth is **enabled by default** in the Docker setup (`ENABLE_PHONE_SIGNUP=true` in `.env`). However, without an SMS provider configured, the Auth service has no way to deliver OTP codes.

## SMS provider configuration

The default `.env.example` and `docker-compose.yml` include commented-out SMS provider placeholders. The example below uses Twilio - you'll need a Twilio account with an account SID, auth token, and message service SID. See [Twilio's documentation](https://www.twilio.com/docs/messaging) for how to obtain these credentials.

To enable SMS delivery:

### Step 1: Uncomment and configure the environment variables

Edit the `.env` file as follows:

```sh name=.env
SMS_PROVIDER=twilio
SMS_OTP_EXP=60
SMS_OTP_LENGTH=6
SMS_MAX_FREQUENCY=60s
SMS_TEMPLATE=Your code is {{ .Code }}

## Twilio credentials
SMS_TWILIO_ACCOUNT_SID=your-account-sid
SMS_TWILIO_AUTH_TOKEN=your-auth-token
SMS_TWILIO_MESSAGE_SERVICE_SID=your-message-service-sid
```

### Step 2: Uncomment the matching lines in Docker Compose configuration

Uncomment the `GOTRUE_SMS_*` lines in the `auth` service's `environment` block:

```yaml name=docker-compose.yml
auth:
  environment:
    # ... existing variables ...
    GOTRUE_SMS_PROVIDER: ${SMS_PROVIDER}
    GOTRUE_SMS_OTP_EXP: ${SMS_OTP_EXP}
    GOTRUE_SMS_OTP_LENGTH: ${SMS_OTP_LENGTH}
    GOTRUE_SMS_MAX_FREQUENCY: ${SMS_MAX_FREQUENCY}
    GOTRUE_SMS_TEMPLATE: ${SMS_TEMPLATE}
    GOTRUE_SMS_TWILIO_ACCOUNT_SID: ${SMS_TWILIO_ACCOUNT_SID}
    GOTRUE_SMS_TWILIO_AUTH_TOKEN: ${SMS_TWILIO_AUTH_TOKEN}
    GOTRUE_SMS_TWILIO_MESSAGE_SERVICE_SID: ${SMS_TWILIO_MESSAGE_SERVICE_SID}
```

### Step 3: Restart the auth service

```sh
sh run.sh recreate auth
```

### Step 4: Verify

```sh
sh run.sh printenv auth | grep GOTRUE_SMS
```

Confirm your provider and credentials appear in the output.

Note: Auth includes built-in integrations for Twilio, Twilio Verify, MessageBird, TextLocal, and Vonage. For providers other than Twilio, add the provider-specific `GOTRUE_SMS_*` lines manually to `docker-compose.yml`.

If you want to use a regional SMS Provider, you can implement the [Send SMS Hook](/docs/guides/self-hosting/self-hosted-auth-hooks#step-by-step-http-endpoint-hook).

## OTP settings

### Expiration

Caution: The default OTP expiration is **60 seconds**. This is often too short for production use, consider increasing it.

Set `SMS_OTP_EXP` in `.env` (value is in seconds):

```sh name=.env
# Set expiration to 5 minutes
SMS_OTP_EXP=300
```

And ensure `GOTRUE_SMS_OTP_EXP: ${SMS_OTP_EXP}` is uncommented in `docker-compose.yml`.

### Length

The default OTP length is 6 digits. You can set it to any value between 6 and 10:

```sh name=.env
SMS_OTP_LENGTH=8
```

### Rate limiting

`SMS_MAX_FREQUENCY` controls the minimum interval between SMS sends to the same phone number. The default is 60 seconds:

```sh name=.env
## Allow one SMS every 30 seconds
SMS_MAX_FREQUENCY=30s
```

## Test OTPs for development

To avoid sending real SMS during development, use `SMS_TEST_OTP` to map phone numbers to fixed OTP codes:

```sh name=.env
SMS_TEST_OTP=16505551234:123456,16505555678:654321
```

Make sure to also uncomment `GOTRUE_SMS_TEST_OTP: ${SMS_TEST_OTP}` in `docker-compose.yml`.

When a test phone number requests an OTP, the Auth service skips SMS delivery and accepts only the mapped code. Other phone numbers continue to use the real SMS provider.

Caution: Remove test OTPs before deploying to production. You can also set an expiration with `SMS_TEST_OTP_VALID_UNTIL` (ISO 8601 datetime, e.g., `2026-12-31T23:59:59Z`) so they stop working automatically.

## Multi-factor authentication (MFA)

The Auth service supports three MFA factor types. Configure them by uncommenting variables in `.env` and the matching `GOTRUE_MFA_*` lines in `docker-compose.yml`.

### App authenticator (TOTP)

TOTP is **enabled by default** - users can enroll with apps like Google Authenticator or Authy without any additional configuration.

To disable TOTP:

```sh name=.env
MFA_TOTP_ENROLL_ENABLED=false
MFA_TOTP_VERIFY_ENABLED=false
```

### Phone MFA

Phone MFA is **disabled by default** (opt-in). It uses the same SMS provider configuration as phone sign-in.

To enable:

```sh name=.env
MFA_PHONE_ENROLL_ENABLED=true
MFA_PHONE_VERIFY_ENABLED=true
```

### Maximum enrolled factors

By default, a user can enroll up to 10 MFA factors. To change this:

```sh name=.env
MFA_MAX_ENROLLED_FACTORS=5
```

## Troubleshooting

### OTP expires too soon

The default `SMS_OTP_EXP` is 60 seconds. Increase it in `.env`:

```sh name=.env
SMS_OTP_EXP=300
```

Ensure `GOTRUE_SMS_OTP_EXP: ${SMS_OTP_EXP}` is uncommented in `docker-compose.yml`, then restart:

```sh
sh run.sh recreate auth
```

### SMS not being delivered

Check the auth container logs for errors:

```sh
docker compose logs auth --tail 50
```

Verify provider credentials reach the container:

```sh
sh run.sh printenv auth | grep GOTRUE_SMS
```

Common causes:

- Provider credentials are in `.env` but the matching `GOTRUE_SMS_*` line is still commented out in `docker-compose.yml`
- Provider credentials are wrong
- Phone number format is wrong (use E.164 format: `+1234567890`)

### Variables added to the environment but not working

Configuration variables from `.env` are **not** automatically available inside the container unless there's a matching passthrough definition in `docker-compose.yml`. Check, e.g., for:

```sh
sh run.sh printenv auth | grep -E 'GOTRUE_SMS|GOTRUE_MFA'
```

After changing the configuration environment variables, recreate the Auth service container:

```sh
sh run.sh recreate auth
```

### Rate limit errors

If users see "rate limit exceeded" errors, check `SMS_MAX_FREQUENCY` (minimum interval between sends) and the global rate limit `GOTRUE_RATE_LIMIT_SMS_SENT` (default: 30 per hour).

### Additional resources

- [Multi-Factor Authentication (Phone)](/docs/guides/auth/auth-mfa/phone)
- [Multi-Factor Authentication (TOTP)](/docs/guides/auth/auth-mfa/totp)
- [Self-hosted Auth hooks](/docs/guides/self-hosting/self-hosted-auth-hooks)
- [Auth server on GitHub](https://github.com/supabase/auth) (check README and `example.env`)
