DEV Community

Cover image for Deploying Logto as an AWS Cognito Alternative
Sanskriti Harmukh for Vultr

Posted on with Aashish Chaurasiya Originally published at docs.vultr.com

Deploying Logto as an AWS Cognito Alternative

AWS Cognito is Amazon's managed authentication service for web and mobile applications, providing User Pools, Identity Pools for federated AWS resource access, a hosted login UI, Lambda triggers, and passwordless sign-in across Lite, Essentials, and Plus tiers. It removes the need to operate an identity store, but it bills per monthly active user (MAU) beyond a free tier of 10,000 MAU that applies only to Lite and Essentials, gates threat protection and adaptive authentication behind the Plus tier (which has no free tier at all), and ties the user directory and its hooks to a specific cloud provider's regions, functions, and IAM. Logto is an open-source Customer Identity and Access Management (CIAM) platform built on OAuth 2.1 and OpenID Connect (OIDC) that covers the same ground on self-hosted infrastructure without per-user billing or cloud provider API dependencies — a branded sign-in experience, social and enterprise SSO federation, multi-factor authentication (MFA), role-based access control (RBAC), organizations for multi-tenancy, and webhooks, all stored in a PostgreSQL database you control. This guide walks through deploying Logto as an alternative to AWS Cognito: the Docker Compose deployment with Traefik and automatic HTTPS, the admin console, sign-in experience and branding, email and social login connectors, enterprise SSO, MFA, RBAC and API resources, organizations, application registration, webhooks, deployment verification, and migration from AWS Cognito. By the end, you'll have a fully configured, self-hosted CIAM platform and a documented path for moving an existing Cognito user base onto it.

Before you begin, you need a Linux-based server with at least 2 CPU cores and 4 GB of RAM as a non-root user with sudo privileges, Docker and Docker Compose installed, and DNS A records pointing to your server's IP address for two subdomains: auth.example.com (Logto Core API and sign-in experience) and admin.example.com (Admin Console).


Understanding Logto Architecture

Logto covers most AWS Cognito components through a self-hosted, OIDC-standard platform. The following table maps each Cognito feature to its Logto counterpart and notes where no equivalent exists.

AWS Cognito Logto Description
Cognito User Pools Logto Users + Sign-in Experience User management and hosted login pages.
Cognito User Pool SAML and OIDC identity providers Logto Enterprise SSO Connectors (SAML/OIDC) Federation with external identity providers.
Cognito Identity Pools No direct equivalent Grants access to AWS resources through IAM roles.
Cognito Hosted UI Logto Sign-in Experience Fully customizable branded authentication flow.
Cognito Social Providers Logto Social Connectors Integration with over 30 social identity providers.
Cognito MFA (SMS, TOTP) Logto MFA TOTP, WebAuthn/Passkeys, SMS, Email OTP, and backup codes.
Cognito Lambda Triggers Logto Webhooks Asynchronous event-driven webhooks (not inline).
Cognito App Clients Logto Applications OIDC clients for SPAs, web apps, and M2M services.
Cognito Groups Logto Roles + Organizations Role-based access control and multi-tenant management.
Cognito Advanced Security No direct equivalent Use WAF, rate limiting, or a reverse proxy for protection.
AWS Amplify SDKs Logto SDKs Official SDKs for over 30 modern development frameworks.

The Logto Core service (auth.example.com) exposes the OIDC endpoints, Management API (/api), and the user-facing sign-in experience. The Admin Console (admin.example.com) is a separate frontend that manages tenant configuration through the Management API.

1. Deploy Logto with Docker Compose

Logto requires PostgreSQL 14 or later to store users, configuration, and session data. This section deploys Logto for production using Docker Compose, Traefik for automatic HTTPS with Let's Encrypt, and PostgreSQL for durable storage.

1. Create the project directory structure:

$ mkdir -p ~/logto/traefik/letsencrypt
Enter fullscreen mode Exit fullscreen mode

2. Navigate to the project directory:

$ cd ~/logto
Enter fullscreen mode Exit fullscreen mode

3. Create the Logto environment file:

$ nano .env
Enter fullscreen mode Exit fullscreen mode

4. Add the following configuration:

# Domain Configuration
LOGTO_CORE_DOMAIN=auth.example.com
LOGTO_ADMIN_DOMAIN=admin.example.com

# Logto Endpoints
ENDPOINT=https://auth.example.com
ADMIN_ENDPOINT=https://admin.example.com

# SSL Configuration
LETSENCRYPT_EMAIL=admin@example.com

# Database
DB_PASSWORD=DB-PASSWORD

# Logto image version
TAG=1.40.1

# Pins the Docker Engine API version to avoid socket communication errors with newer Traefik releases
DOCKER_API_VERSION=1.54
Enter fullscreen mode Exit fullscreen mode

Replace auth.example.com and admin.example.com with your subdomains, admin@example.com with your Let's Encrypt notification address, DB-PASSWORD with a strong PostgreSQL password, and 1.40.1 with the Logto release you want to deploy (check the Logto releases page for the latest stable version).

5. Download the Logto Docker Compose file for the pinned release. Pin the compose file URL to the same release tag as TAG in the environment file — this file is maintained by Logto.

$ wget https://raw.githubusercontent.com/logto-io/logto/v1.40.1/docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

6. Update the docker-compose.yml file for production use. The official compose file ships with hardcoded database credentials labeled for demonstration; replace those defaults before deployment.

$ nano docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

Find the app service and update the DB_URL under environment:

- DB_URL=postgres://postgres:${DB_PASSWORD}@postgres:5432/logto
Enter fullscreen mode Exit fullscreen mode

Find the postgres service and update POSTGRES_PASSWORD:

POSTGRES_PASSWORD: ${DB_PASSWORD}
Enter fullscreen mode Exit fullscreen mode

Pin the PostgreSQL image to a specific release for reproducible deployments:

image: postgres:17.5-alpine
Enter fullscreen mode Exit fullscreen mode

7. Create the Traefik Docker Compose override file:

$ nano traefik/docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

8. Add the following configuration:

services:
  traefik:
    image: traefik:v3.7.0
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.myresolver.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
    ports:
      - "80:80"
      - "443:443"
    environment:
      - DOCKER_API_VERSION=${DOCKER_API_VERSION}
      - TRUST_PROXY_HEADER=true
    volumes:
      - ./letsencrypt:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      default:
        aliases:
          - ${LOGTO_CORE_DOMAIN}
          - ${LOGTO_ADMIN_DOMAIN}
  app:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.logto-core.rule=Host(`${LOGTO_CORE_DOMAIN}`)"
      - "traefik.http.routers.logto-core.entrypoints=websecure"
      - "traefik.http.routers.logto-core.tls.certresolver=myresolver"
      - "traefik.http.routers.logto-core.service=logto-core"
      - "traefik.http.services.logto-core.loadbalancer.server.port=3001"
      - "traefik.http.routers.logto-admin.rule=Host(`${LOGTO_ADMIN_DOMAIN}`)"
      - "traefik.http.routers.logto-admin.entrypoints=websecure"
      - "traefik.http.routers.logto-admin.tls.certresolver=myresolver"
      - "traefik.http.routers.logto-admin.service=logto-admin"
      - "traefik.http.services.logto-admin.loadbalancer.server.port=3002"
Enter fullscreen mode Exit fullscreen mode

Traefik routes HTTPS traffic to Logto on subdomain-based host rules — port 3001 serves the Core API and sign-in experience, port 3002 serves the Admin Console. Let's Encrypt certificates are provisioned automatically once DNS resolves to this server. This stack pins svhd/logto to the TAG value in .env, traefik to v3.7.0, and PostgreSQL to 17.5-alpine.

9. Start the Logto stack:

$ docker compose -f docker-compose.yml -f traefik/docker-compose.yml up -d
Enter fullscreen mode Exit fullscreen mode

10. Verify the containers are running:

$ docker ps
Enter fullscreen mode Exit fullscreen mode

The output displays running containers for Traefik, Logto, and PostgreSQL.

11. Verify HTTPS on the Admin Console:

$ curl -sI https://admin.example.com
Enter fullscreen mode Exit fullscreen mode

The output returns HTTP/2 302 with location: /console/welcome on first boot.

12. Verify the OIDC discovery endpoint:

$ curl -s https://auth.example.com/oidc/.well-known/openid-configuration | head -c 200
Enter fullscreen mode Exit fullscreen mode

The output returns JSON containing authorization_endpoint and issuer values scoped to your auth.example.com domain.

2. Access the Admin Console

The Admin Console is the central management interface for configuring the Logto tenant.

  1. Open a web browser and navigate to the Logto Admin Console at https://admin.example.com.
  2. Follow the on-screen instructions to create the initial administrator account, then log in with the new credentials.
  3. Review the sidebar to confirm access to Applications, Sign-in & account, Multi-factor auth, Connectors, Enterprise SSO, API resources, Roles, Organizations, Webhooks, and Audit logs.

3. Configure the Sign-in Experience

The sign-in experience defines the look, feel, and authentication flow for your users.

Set the brand identity: navigate to Sign-in & account in the left sidebar (on first visit, click Get started then Got it), upload your logo (for example, https://example.com/logo.png) and set the primary brand color (for example, #6139F6) under Branding area, then click Done.

Configure the email connector: navigate to Connectors and select the Email and SMS connectors tab, click Set up under the Email connector and select SMTP. Obtain the SMTP host, port, username, and password from your email provider, and enter them in the Logto Console:

  • host: SMTP-HOST
  • Port: SMTP-PORT
  • Auth: type: login, user: SMTP-USERNAME, pass: SMTP-PASSWORD
  • From email: SENDER-EMAIL-ADDRESS
  • Reply to: REPLY-TO-EMAIL-ADDRESS

Toggle Secure to On if your provider requires TLS, send a test email, then click Save and Done.

Define sign-up identifiers and sign-in methods: on Sign-in & account → Sign-up and sign-in, select sign-up identifiers (Username, Email address, Phone number), configure sign-in methods, and click Save Changes.

Enable passwordless sign-in: on the same tab, enable Email verification code or SMS verification code under Sign-in methods, ensure the corresponding connector is configured, then click Save Changes.

Preview the sign-in UI: click Live preview on the Sign-up and sign-in page to test the branded interface and enabled sign-in methods before applying changes to production traffic.

Logto also supports SMS-based registration and sign-in via third-party providers such as Twilio or Vonage — configure an SMS connector the same way as the email connector. Users access the hosted sign-in UI through a registered application with a valid client_id and redirect URI, not by visiting auth.example.com directly; direct navigation without an application context returns an unknown-session page.

4. Set Up Social Login Connectors

  1. Navigate to Connectors in the left sidebar and select the Social connectors tab, then click Add Social Connector and select Google.
  2. Create a project in the Google Cloud Console: create a new project, go to APIs & Services → OAuth consent screen, configure the consent screen and add your domain (for example, example.com) as an authorized domain, go to Credentials → Create Credentials → OAuth client ID, select Web application, and add the Logto callback URI (for example, https://auth.example.com/callback/google) to Authorized redirect URIs.
  3. Copy the Client ID and Client Secret from Google and paste them into the Logto social connector configuration.
  4. Configure the Scope field — Logto requests openid, profile, and email by default; append additional scope URLs if the application needs more Google API access.
  5. Click Save and Done, then repeat the process for GitHub and Apple.
  6. On Sign-in & account → Sign-up and sign-in, under Social sign-in, click Add Social Connector and select the connectors you configured, then Save Changes and test with Live preview.

5. Configure Enterprise SSO

Enterprise Single Sign-On (SSO) enables corporate users to authenticate via their own Identity Provider (IdP), such as Okta or Microsoft Entra ID.

  1. Navigate to Enterprise SSO, click Add enterprise connector, and select Okta (or SAML / OIDC for a generic connector). Use the Connection guide button for provider-specific setup instructions.
  2. In your Okta dashboard, register a new OIDC application, set the Login redirect URI to the value provided by Logto, and obtain the Client ID, Client Secret, and Issuer URL.
  3. Enter these details into the Logto connector configuration and click Save. For SAML connectors, exchange IdP metadata by uploading the metadata file or entering the metadata URL.
  4. On the SSO Experience tab, add the email domains associated with the organization (for example, example.com) — users with matching email domains are automatically routed to the enterprise IdP login page, and other sign-in methods are disabled for those domains.
  5. Enable JIT provisioning to create users automatically on first SSO sign-in, then click Save changes.

6. Enable Multi-Factor Authentication

  1. Navigate to Multi-factor auth in the left sidebar.
  2. Toggle the desired MFA methods to On: Authenticator app (TOTP), Passkeys (WebAuthn/biometric/hardware key), Backup codes, and SMS or Email verification code (requires the corresponding connector).
  3. Configure the MFA Policy (optional MFA or required on every login), then click Save changes.

7. Configure RBAC and API Resources

  1. Navigate to API Resources, click Create API resource, enter an API Name and a unique API Identifier (for example, https://api.example.com/orders), and define Permissions (Scopes) such as read:orders and write:orders.
  2. Navigate to Roles, click Create role, assign the API permissions to the role, and click Save. On the Assign user screen, assign users now or click Skip to assign them later from User management.
  3. If global roles must appear in access tokens, navigate to Custom JWT and configure an access token script to inject role claims into issued JWTs.
  4. Open the API resource you created and click Check guide to view framework-specific integration code (Express, Python, Spring Boot, and others) for initializing the Logto SDK, protecting API routes, and validating the access token.

8. Set Up Organizations for Multi-Tenancy

Organizations offer multi-tenant isolation for B2B applications where users belong to different corporate entities. An organization template is a blueprint defining a consistent set of roles and permissions available to every organization in your Logto tenant.

  1. Navigate to Organization template and create Organization permissions (fine-grained, non-API actions such as read:resource, edit:resource, delete:resource).
  2. Create Organization roles (for example, Admin or Member) and map the permissions to each role.
  3. Navigate to Organizations, click Create organization, select it, and go to the Members tab.
  4. Click Add Members, assign an organization role to each member from the dropdown — Logto scopes JWT tokens to include the organization context when organization-scoped tokens are requested.
  5. Click Check guide to view integration tutorials for multi-tenant features and org-scoped tokens.

9. Register Applications

Register your frontend and backend services as applications within Logto to authenticate users and obtain tokens. Specific steps vary by application type — Single-Page App, Traditional Web App, Native App, or Machine-to-Machine — and the console provides a framework-specific tutorial for each.

  1. Navigate to Applications. If none exist, select a supported framework (React, Next.js, Node.js, and others) and click Start building; otherwise click Create application.
  2. Enter your application name, click Create application, and follow the Jumpstart guide to integrate Logto.
  3. Configure the Redirect URIs and Post Sign-out Redirect URIs, note the App ID and integration endpoint, then click Save changes.

Register at least three application types to cover common architectures: a Single-Page App (SPA) for React/Vue/Angular frontends, a Traditional Web App for server-rendered applications with a backend session, and a Machine-to-Machine (M2M) application for backend services that call the Management API without user interaction. Open the application sign-in URL or use the Logto SDK in a test client to confirm the redirect-based sign-in flow completes successfully.

10. Configure Webhooks

  1. Navigate to Webhooks, click Create webhook, enter the backend Endpoint URL and a descriptive name.
  2. Select the Webhook events to monitor (for example, User.Created, PostSignIn, User.Data.Updated), add optional Custom headers if needed, and click Create webhook.
  3. Use the Signing Key shown in the webhook settings to cryptographically verify that incoming payloads originated from your Logto instance, then send a test payload to confirm the endpoint receives and processes events.

11. Verify the Deployment

  • Infrastructure: Confirm all containers are running with docker ps — Traefik, Logto, and PostgreSQL should report an Up status.
  • TLS: Open https://admin.example.com and https://auth.example.com in a browser; both should load with valid Let's Encrypt certificates.
  • OIDC discovery: Run curl -s https://auth.example.com/oidc/.well-known/openid-configuration and confirm the issuer matches https://auth.example.com/oidc.
  • Admin Console: Create the initial admin account and confirm the dashboard loads with all sidebar sections.
  • User registration and sign-in: Register a test user through your application and confirm password-based sign-in works.
  • Social login, MFA, RBAC, and organizations: Test each configured method end to end — social sign-in through Live preview, MFA enforcement on a test user's next sign-in, an access token containing expected scopes after role assignment, and organization context appearing in the token after adding a user to an organization.
  • Audit logs: Navigate to Audit logs in the Admin Console and confirm sign-in and configuration events are recorded.

12. Migrate from AWS Cognito to Logto

Migrating from AWS Cognito to Logto involves mapping User Pool resources to Logto tenants, replacing Amplify or Cognito SDK calls with Logto SDKs, and converting Lambda triggers to webhooks or Custom JWT scripts. Because Cognito does not export password hashes or MFA secrets, most migrations follow a bulk user import with mandatory re-authentication strategy. The steps below assume you already completed the deployment, admin console, and application registration sections above.

User migration. An AWS Cognito User Pool bundles user directories, authentication flows, app clients, and groups into a single managed resource; Logto splits these across users, the sign-in experience, applications, roles, and organizations.

  • Export: retrieve user profiles from Cognito using the AWS CLI (list-users) or the ListUsers API.
  • Import: create users through the Logto Management API (POST /api/users at https://auth.example.com/api), authenticated with an access token from a Logto Machine-to-Machine application.
  • Passwords: Cognito does not expose password hashes — import users with temporary credentials and trigger a password reset on first login.
  • MFA enrollments: TOTP seeds and WebAuthn credentials cannot be exported; users must re-enroll after migration.
  • Identity mapping: store the original Cognito sub UUID in Logto customData (for example, legacy_cognito_sub) to preserve linkages in downstream application databases.

Application migration. If applications use the AWS Amplify Auth library, replace Amplify Auth APIs with the Logto SDK: signInWithPassword/embedded forms become redirect-based Universal Login; the User Pool App Client ID becomes the Logto Application ID (client_id); Callback URLs become Redirect URIs and Post Sign-out Redirect URIs; token refresh moves to the Logto SDK's session management or standard OIDC refresh flows. If applications call the Cognito API directly, replace InitiateAuth/AdminInitiateAuth with the OIDC authorization code flow (PKCE) or client credentials flow (M2M), GetUser with the OIDC UserInfo endpoint or Management API user lookup, and validate access tokens against the Logto JWKS endpoint (https://auth.example.com/oidc/jwks, issuer https://auth.example.com/oidc) instead of Cognito JWTs.

Role and group migration. Cognito Groups that gate API access or admin capabilities map to global Logto Roles: create each role (POST /api/roles), define API resource permissions, and assign users (POST /api/users/{userId}/roles) — configure a Custom JWT script to inject role claims into access tokens, since global roles aren't included by default. Cognito Groups that represent B2B tenants map to Logto Organizations: create the organization (POST /api/organizations), define organization roles in the Organization template, and add members (POST /api/organizations/{id}/users). Update backend route guards to parse the claims your Custom JWT script emits instead of Cognito's cognito:groups claim.

Lambda trigger migration. Post-confirmation/post-authentication triggers (async) map to Logto Webhooks (User.Created, PostSignIn, User.Data.Updated). Pre-token generation triggers (sync, claims modification) map to Custom JWT access token scripts. Pre-authentication/pre-sign-up triggers (sync, blocking) map to application middleware, API gateway rules, or a custom authentication proxy — Logto webhooks cannot block or reject an in-progress sign-in synchronously. Custom message triggers map to email/SMS connectors or a webhook consumer. Unlike Lambda triggers, which scale per invocation, Custom JWT scripts run on every token issuance and webhook consumers run as always-on services — plan capacity accordingly.

Data storage considerations. Cognito sessions and refresh tokens become invalid at cutover, so force re-authentication and clear local session caches. Applications that store Cognito sub values rely on the legacy_cognito_sub mapping in Logto customData until updated. Replace calls to Cognito IDP endpoints (AdminGetUser, ListUsers, AdminUpdateUserAttributes) with the Logto Management API at https://auth.example.com/api. Recreate any Cognito SAML/OIDC federation as Logto Enterprise SSO or social connectors.

Things to take care of during migration: moving embedded Amplify sign-in forms to redirect-based OIDC is the largest application-code change in most migrations; Logto webhooks are asynchronous only, so blocking validation needs middleware or a gateway; Cognito Identity Pools (AWS resource access via IAM) have no Logto equivalent, so provide access through M2M API gateways, role assumption, or application-level credentials; Cognito Advanced Security has no direct Logto OSS equivalent, so implement rate limiting or WAF rules at the reverse proxy; MFA secrets can't be extracted, so users must re-enroll; Cognito's custom claims (cognito:groups, cognito:username) differ from Logto's standard OIDC claims (sub, email, scope); Hosted UI CSS customization maps to Logto sign-in experience branding, though complex custom login pages need a custom UI built on the Logto SDK; and Cognito's per-MAU pricing changes to an infrastructure-based cost model, so calculate total cost of ownership before cutover. Run a phased migration: import users first, validate token flows in a staging tenant, then redirect production traffic to Logto and disable the Cognito User Pool.

Next Steps

  • Configure a Custom JWT script to enrich access tokens with the role and organization claims your applications expect.
  • Set up organization-scoped applications for each B2B tenant migrating off Cognito Groups.
  • Add rate limiting or WAF rules at the reverse proxy layer to replace Cognito Advanced Security.
  • Run a staging-tenant migration rehearsal before cutting production traffic over from Cognito.

For the full guide with additional tips, visit the original article on Vultr Docs.

Top comments (0)