Skip to main content

Configure Phone Sign-in & MFA

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 and Multi-Factor Authentication.

Before you begin

You need:

  • A working self-hosted Supabase installation. See Self-Hosting with 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 for how to obtain these credentials.

To enable SMS delivery:

Step 1: Uncomment and configure the environment variables

Edit the .env file as follows:

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:

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 run.sh recreate auth

Step 4: Verify

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.

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):

# 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:

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:

## 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:

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:

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:

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:

MFA_MAX_ENROLLED_FACTORS=5

Troubleshooting

OTP expires too soon

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

SMS_OTP_EXP=300

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

sh run.sh recreate auth

SMS not being delivered

Check the auth container logs for errors:

docker compose logs auth --tail 50

Verify provider credentials reach the container:

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 run.sh printenv auth | grep -E 'GOTRUE_SMS|GOTRUE_MFA'

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

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