English
Get started

← All templates · Passwords and sign-in

Deploy FusionAuth in one click

Customer identity and access management: sign-in, registration, single sign-on and MFA for your apps

A self-hosted alternative to Auth0, Okta, and Amazon Cognito.

What is FusionAuth?

FusionAuth is a customer identity and access management (CIAM) server: your web, mobile and API apps hand sign-in, registration and user management to it. It speaks OAuth 2.0, OpenID Connect and SAML 2.0, so most frameworks connect to it with their standard libraries, and it has client libraries of its own for JavaScript, Python, Java, Go, PHP, .NET, Ruby and more. Its hosted login pages sign people in with a password, a magic link or a social account such as Google, Apple or Facebook, add two-factor authentication with an authenticator app, and can be themed to look like your app.

FusionAuth is made by FusionAuth, Inc. of Denver, and its Community plan is free with no limit on users; paid plans add features such as email and SMS codes, breached password detection, custom registration forms and SCIM. It is not open source: you run it under FusionAuth's license. Everything is in one admin UI and a REST API: applications, tenants for separate user pools, roles, groups, webhooks, JWT settings and lambdas that shape tokens.

This template runs FusionAuth's official image, version 1.69.4, with a Postgres 18 database that New project makes beside it, or one of yours that you pick, and searches users in Postgres, with no Elasticsearch. FusionAuth first starts on a port nobody reaches, where its administrator is made from the email and password you type, so its setup wizard is never open to a stranger. Its email goes through Resend, SendGrid or Amazon SES, whichever you pick in New project, or nowhere until you set a provider on the tenant.

Hosting FusionAuth on Pethost

  1. You sign in to Pethost with a passkey, GitHub or Google. No machine yet? You pick a plan, and the link waits until it is ready.
  2. New project opens at FusionAuth. You fill in what it asks for and click Create project: Email, Admin password.
  3. Email is optional: for FusionAuth to send it, you pick Resend, SendGrid, or Amazon SES in New project and paste in what it gives you.
  4. New project also makes a new database for FusionAuth, and links it before the app first starts: Postgres (512 MB). Or you pick one of your Postgres databases: FusionAuth gets a user and a database of its own in it, which the other apps there cannot read, and the apps in it are backed up and restored together.
  5. In a minute or two FusionAuth is live at its own address with HTTPS. Its secrets are made at random, and its data is in the nightly backup.
  6. It is a project like any other: you or your agent can change it, roll it back or restore it.

Questions about FusionAuth

Is FusionAuth free?

FusionAuth's Community plan is free to self-host, with no limit on users and the core features: OAuth 2.0, OpenID Connect and SAML, social login, authenticator-app MFA, themes and the full API. Paid plans add email and SMS codes, breached password detection, custom registration forms, SCIM and threat detection. FusionAuth is free to use but not open source.

FusionAuth vs Auth0: which should I use?

Both sign users in over OAuth 2.0, OpenID Connect and SAML, with social login and MFA. Auth0 is a cloud service priced by monthly active users; FusionAuth's Community plan runs where you host it, free for any number of users, and its data stays in your database.

How do I add login to my app with FusionAuth?

Create an application in FusionAuth's admin UI and add your app's callback address as an authorized redirect URL. Your app then sends users to FusionAuth's hosted login page with an OpenID Connect library, using the application's client id and secret, and gets back their tokens.

Does FusionAuth support social login and passkeys?

Yes. Identity Providers adds Google, Apple, Facebook, Twitter and any OpenID Connect or SAML 2.0 provider to the login page in the Community plan. Passkeys (WebAuthn) need a free Licensed Community plan, registered at FusionAuth's account portal.

Can FusionAuth migrate users from Auth0 or Firebase?

Yes. Its User Import API takes existing password hashes, so users keep their passwords, and FusionAuth documents migrations from Auth0, Amazon Cognito, Firebase, Keycloak and others.

How much memory and disk does FusionAuth need?

FusionAuth runs on Java: about 600–800 MB of memory at rest, beside its Postgres of 512 MB, more with many users and logins; its database grows slowly, with the users and their login records. New project gives each of its databases the memory the template recommends, unless you change it: Postgres (512 MB). On Pethost the Starter plan is enough for it, with room for other apps beside it.

FusionAuth Docker Compose file

The template is this compose.yaml and the variables written into the project’s .env as it is created. Pethost runs the file as it is written. The machine writes how the app signs in to its databases there, as it links them. New project writes the email settings picked in it, or leaves them empty when the app sends none.

Containers: fusionauth (fusionauth/fusionauth-app:1.69.4).

Databases, each a project of its own: Postgres (512 MB).

services:
  fusionauth:
    image: fusionauth/fusionauth-app:1.69.4
    restart: unless-stopped
    environment:
      # FusionAuth reads DATABASE_URL as a JDBC URL, so it is made here from the part's parts.
      DATABASE_URL: jdbc:postgresql://${DATABASE_HOST:?reload New project to link a Postgres database, or send link_databases with env_prefix DATABASE}:${DATABASE_PORT:?reload New project to link a Postgres database, or send link_databases with env_prefix DATABASE}/${DATABASE_NAME:?reload New project to link a Postgres database, or send link_databases with env_prefix DATABASE}
      DATABASE_USERNAME: ${DATABASE_USER:?reload New project to link a Postgres database, or send link_databases with env_prefix DATABASE}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD:?reload New project to link a Postgres database, or send link_databases with env_prefix DATABASE}
      # Silent mode makes the schema in the linked database, which the app's user owns, and
      # applies a new release's migrations at its start; production mode never shows the
      # migration screen to a visitor.
      FUSIONAUTH_APP_SILENT_MODE: "true"
      FUSIONAUTH_APP_RUNTIME_MODE: production
      # Users are searched in Postgres, with no Elasticsearch or OpenSearch beside it.
      SEARCH_TYPE: database
      FUSIONAUTH_APP_MEMORY: 512M
      # 20 connections at most, FusionAuth's default, within the 47 of the Postgres part.
      DATABASE_MAXIMUM_POOL_SIZE: "20"
      URL: ${URL}
      ADMIN_EMAIL: ${ADMIN_EMAIL}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      SETUP_API_KEY: ${SETUP_API_KEY}
      # The mail provider picked in New project, by STARTTLS on its port; with none, FusionAuth
      # sends no mail until one is set on the tenant.
      SMTP_HOST: ${SMTP_HOST}
      SMTP_PORT: ${SMTP_PORT}
      SMTP_USER: ${SMTP_USER}
      SMTP_PASSWORD: ${SMTP_PASSWORD}
      SMTP_FROM: ${SMTP_FROM}
      SMTP_FROM_NAME: ${SMTP_FROM_NAME}
    # A new FusionAuth shows its setup wizard to whoever opens it first, also in the seconds
    # before Kickstart has run, and counts the setup as done only when Kickstart made the
    # administrator. So FusionAuth runs first on port 9019, which nobody reaches, and only then on
    # port 9011, the page's. On a new database, Kickstart makes the set-up's API key and the
    # administrator, with a stand-in email and password, and gives the default tenant the
    # project's address as its issuer; the API then gives the administrator the email and
    # password of the fields, and the tenant the mail provider, since Kickstart takes a quote or
    # a backslash out of what it is given. The set-up's key is deleted last: while it answers,
    # the set-up is unfinished, and once it is refused and the setup wizard sends a visitor to
    # sign in, FusionAuth is set up.
    command:
      - bash
      - -c
      - |
        set -e
        cd /usr/local/fusionauth
        json() { printf '%s' "$$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'; }
        # The set-up's API key, and the ids of it and of the administrator, all from SETUP_API_KEY.
        key=$$SETUP_API_KEY
        key_id=$${key:0:8}-$${key:8:4}-$${key:12:4}-$${key:16:4}-$${key:20:12}
        admin_id=$${key:32:8}-$${key:40:4}-$${key:44:4}-$${key:48:4}-$${key:52:12}

        # Kickstart runs only on a FusionAuth with no API key and no user yet.
        cat > /tmp/kickstart.json <<KICKSTART
        {
          "apiKeys": [{"id": "$$key_id", "key": "$$key", "keyManager": true, "description": "The template's set-up"}],
          "requests": [
            {"method": "PATCH", "url": "/api/tenant/#{FUSIONAUTH_TENANT_ID}", "body": {"tenant": {"issuer": "$$URL"}}},
            {
              "method": "POST",
              "url": "/api/user/registration/$$admin_id",
              "body": {
                "user": {"email": "[email protected]", "password": "$$key"},
                "registration": {"applicationId": "#{FUSIONAUTH_APPLICATION_ID}", "roles": ["admin"]}
              }
            }
          ]
        }
        KICKSTART
        FUSIONAUTH_APP_HTTP_PORT=9019 FUSIONAUTH_APP_KICKSTART_FILE=/tmp/kickstart.json fusionauth-app/bin/start.sh &
        first=$$!
        stop_first() {
          kill $$first
          wait $$first 2> /dev/null || true
        }
        api() {
          method=$$1
          path=$$2
          shift 2
          curl -sS -o /tmp/api.json -w '%{http_code}' -X "$$method" -H "Authorization: $$key" \
            -H 'Content-Type: application/json' "$$@" "http://127.0.0.1:9019$$path" 2> /dev/null || true
        }
        refused() {
          echo "FusionAuth refused $$1: $$(cat /tmp/api.json)" >&2
          stop_first
          exit 1
        }

        # Silent mode migrates the database first; a database that does not answer stops the
        # start after 5 minutes, and the next start tries again.
        for wait in $$(seq 300); do
          kill -0 $$first 2> /dev/null || { echo "FusionAuth stopped at its first start." >&2; exit 1; }
          admin=$$(api GET "/api/user/$$admin_id")
          [ "$$admin" = 200 ] && break
          wizard=$$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:9019/admin/setup-wizard || true)
          [ "$$admin" = 401 ] && [ "$$wizard" = 302 ] && break
          sleep 1
        done
        if [ "$$admin" != 200 ] && [ "$$wizard" != 302 ]; then
          echo "FusionAuth did not answer on its first start within 5 minutes." >&2
          stop_first
          exit 1
        fi

        if [ "$$admin" = 200 ]; then
          echo "setting FusionAuth up on a port nobody reaches"
          tenant=$$(grep -o '"tenantId":"[^"]*"' /tmp/api.json | head -n 1 | cut -d '"' -f 4)
          if [ "$${#ADMIN_PASSWORD}" -lt 8 ]; then
            echo "The admin password has fewer than 8 characters, which FusionAuth refuses: give one of 8 or more in ADMIN_PASSWORD of the project's .env, then deploy again." >&2
            stop_first
            exit 1
          fi
          [ "$$(api PATCH "/api/user/$$admin_id" \
            -d "{\"user\": {\"email\": \"$$(json "$$ADMIN_EMAIL")\", \"password\": \"$$(json "$$ADMIN_PASSWORD")\"}}")" = 200 ] \
            || refused "the administrator's email and password"
          if [ -n "$$SMTP_HOST" ]; then
            [ "$$(api PATCH "/api/tenant/$$tenant" -d "{\"tenant\": {\"emailConfiguration\": {\"host\": \"$$(json "$$SMTP_HOST")\",
              \"port\": $$SMTP_PORT, \"security\": \"TLS\", \"username\": \"$$(json "$$SMTP_USER")\", \"password\": \"$$(json "$$SMTP_PASSWORD")\",
              \"defaultFromEmail\": \"$$(json "$$SMTP_FROM")\", \"defaultFromName\": \"$$(json "$$SMTP_FROM_NAME")\"}}}")" = 200 ] \
              || refused "the mail provider"
          fi
          # The set-up's API key can do anything, and nothing needs it any more.
          [ "$$(api DELETE "/api/api-key/$$key_id")" = 200 ] || refused "to delete the set-up's API key"
          echo "FusionAuth is set up, with the administrator $$ADMIN_EMAIL"
        fi
        stop_first
        rm /tmp/kickstart.json

        exec fusionauth-app/bin/start.sh
    healthcheck:
      test: ["CMD", "curl", "-fsS", "-o", "/dev/null", "http://127.0.0.1:9011/api/status"]
      interval: 10s
      timeout: 5s
      retries: 30
      start_period: 300s

More in Passwords and sign-in