Docker

This page explains how to install FusionAuth using Docker.

The instructions below require the following prerequisites:

  • Docker 23 or later
  • On macOS and Windows, one of the following container management tools:
    • OrbStack (to use Orbstack for docker compose commands after install, run docker context use orbstack)
    • Podman (in the commands below, replace docker with podman)
    • Docker desktop

For Kubernetes install instructions, see Install with Kubernetes. For OpenShift and other community-supported environments, see the FusionAuth Contrib GitHub repo.

For more FusionAuth Docker images, see fusionauth on Docker Hub.

Docker Compose#

Use the steps below to install FusionAuth using Docker Compose using the configuration files in the fusionauth-containers repository. For an in-depth example, see the FusionAuth Docker Compose example repository.

Docker Services#

This example configuration includes the following services:

One server

FusionAuth Service#

This is the service that runs the FusionAuth application.

Review the Configuration documentation to customize your deployment. The best way to configure FusionAuth when using Docker is to use environment variables as documented in that link.

By default, this configuration exposes port 9011 to your host machine.

Database Service#

The database service provides a PostgreSQL database for use by FusionAuth.

Either set the POSTGRES_PASSWORD environment variable in the db service section, or, more ideally, set the value in the host environment and only reference it in the docker-compose.yml file.

By default this database is not accessible outside of the Docker Compose containers.

Search Service#

The search service provides a searchable index of users and field data.

Custom Docker Images#

If you want to build your own image starting with our base image, start with the fusionauth/fusionauth-app image. Just as the FusionAuth Docker image is based on an Ubuntu container image, you can build a Docker file which is based on the fusionauth/fusionauth-app:latest image. This can be useful if you want to permanently add a password hashing plugin, configuration file, or other customization to the image.

Here's a Dockerfile which extends the latest FusionAuth image:

Example Dockerfile for building fusionauth-app including a plugin

FROM maven AS build
WORKDIR /app
RUN git init . &&\
  git remote add -t \* -f origin https://github.com/FusionAuth/fusionauth-example-password-encryptor.git &&\
  git checkout master &&\
  mvn compile package

FROM fusionauth/fusionauth-app:latest
COPY --chown=fusionauth:fusionauth --from=build /app/target/fusionauth-example-password-encryptor*.jar /usr/local/fusionauth/plugins/

Here's an example docker compose YAML file which uses this new image:

Example docker-compose.yml for building fusionauth-app including a plugin

version: '3'

services:
  db:
    image: postgres:16.0-bookworm
    environment:
      PGDATA: /var/lib/postgresql/data/pgdata
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -U postgres" ]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - db_net
    restart: unless-stopped
    volumes:
      - db_data:/var/lib/postgresql/data

  search:
    image: opensearchproject/opensearch:2.11.0
    environment:
      cluster.name: fusionauth
      discovery.type: single-node
      node.name: search
      plugins.security.disabled: true
      bootstrap.memory_lock: true
      OPENSEARCH_JAVA_OPTS: ${OPENSEARCH_JAVA_OPTS}
    healthcheck:
      interval: 10s
      retries: 80
      test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:9200/
    restart: unless-stopped
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    ports:
      - 9200:9200 # REST API
      - 9600:9600 # Performance Analyzer
    volumes:
      - search_data:/usr/share/opensearch/data
    networks:
      - search_net

  fusionauth:
    #image: fusionauth/fusionauth-app:latest
    build: ./fusionauth-app
    depends_on:
      db:
        condition: service_healthy
      search:
        condition: service_healthy
    environment:
      DATABASE_URL: jdbc:postgresql://db:5432/fusionauth
      DATABASE_ROOT_USERNAME: ${POSTGRES_USER}
      DATABASE_ROOT_PASSWORD: ${POSTGRES_PASSWORD}
      DATABASE_USERNAME: ${DATABASE_USERNAME}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      FUSIONAUTH_APP_MEMORY: ${FUSIONAUTH_APP_MEMORY}
      FUSIONAUTH_APP_RUNTIME_MODE: ${FUSIONAUTH_APP_RUNTIME_MODE}
      FUSIONAUTH_APP_URL: http://fusionauth:9011
      SEARCH_SERVERS: http://search:9200
      SEARCH_TYPE: elasticsearch
      FUSIONAUTH_APP_KICKSTART_FILE: ${FUSIONAUTH_APP_KICKSTART_FILE}
      FUSIONAUTH_APP_INSTALLATION_SOURCE: fusionauth-example-docker-compose
    networks:
      - db_net
      - search_net
    restart: unless-stopped
    ports:
      - 9011:9011
    volumes:
      - fusionauth_config:/usr/local/fusionauth/config
      - ./kickstart:/usr/local/fusionauth/kickstart

networks:
  db_net:
    driver: bridge
  search_net:
    driver: bridge

volumes:
  db_data:
  fusionauth_config:
  search_data:

With this example, you can use docker compose build to only run the build steps in the referenced Dockerfile. This will create you a custom Docker image which is consequentially used in the creation of the container in Docker Compose when running docker compose up -d. Alternatively you can run only docker compose up -d which will automatically take care of the build as well if not present.

By default the build process will cache a lot of the steps, to force a fresh build you can run docker compose build --pull --no-cache instead.

Here's the FusionAuth application Dockerfile as a reference which builds the fusionauth/fusionauth-app base image.

The FusionAuth Docker file

#
# FusionAuth App Dockerfile
#
# Build:
#   > docker pull ubuntu:noble
#   > docker buildx build --platform=linux/arm64 -t fusionauth/fusionauth-app:1.64.1 .
#   > docker buildx build --platform=linux/arm64 -t fusionauth/fusionauth-app:latest .
#
# Note: Substitute your target platform architecture. The above example is targetting a 64-bit ARM platform.
#       To target an Intel based platform use --platform=linux/amd64.
#
# Run:
#  > docker run -p 9011:9011 -it fusionauth/fusionauth-app
#
# Publish:
#   > docker push fusionauth/fusionauth-app:1.64.1
#   > docker push fusionauth/fusionauth-app:latest
#

###### Setup the java and fusionauth-app base #####################################################
FROM --platform=$BUILDPLATFORM ubuntu:noble AS build

ARG BUILDPLATFORM
ARG FUSIONAUTH_VERSION=1.64.1
ARG JDK_MODULES=java.base,java.compiler,java.desktop,java.instrument,java.logging,java.management,java.naming,java.net.http,java.rmi,java.security.jgss,java.security.sasl,java.sql,java.xml.crypto,jdk.attach,jdk.crypto.ec,jdk.dynalink,jdk.jcmd,jdk.jdi,jdk.localedata,jdk.jpackage,jdk.unsupported,jdk.zipfs
ARG TARGETPLATFORM
ARG TARGETARCH
RUN printf "Building on ${BUILDPLATFORM} for ${TARGETPLATFORM} (${TARGETARCH})."
RUN case "${BUILDPLATFORM}" in \
    linux/arm64)\
        BUILD_JAVA_SUM="d768eecddd7a515711659e02caef8516b7b7177fa34880a56398fd9822593a79";\
        BUILD_JAVA_URL="https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.4%2B7/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.4_7.tar.gz";\
        ;;\
    linux/amd64)\
        BUILD_JAVA_SUM="51fb4d03a4429c39d397d3a03a779077159317616550e4e71624c9843083e7b9";\
        BUILD_JAVA_URL="https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.4%2B7/OpenJDK21U-jdk_x64_linux_hotspot_21.0.4_7.tar.gz";\
        ;;\
    *)\
        printf "Unsupported build platform arch: ${BUILDPLATFORM}";\
        exit 1;\
        ;;\
    esac \
    && case "${TARGETARCH}" in \
    arm64)\
        JAVA_SUM="d768eecddd7a515711659e02caef8516b7b7177fa34880a56398fd9822593a79";\
        JAVA_URL="https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.4%2B7/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.4_7.tar.gz";\
        ;;\
    ppc64le)\
        JAVA_SUM="c208cd0fb90560644a90f928667d2f53bfe408c957a5e36206585ad874427761";\
        JAVA_URL="https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.4%2B7/OpenJDK21U-jdk_ppc64le_linux_hotspot_21.0.4_7.tar.gz";\
        ;;\
    s390x)\
        JAVA_SUM="c900c8d64fab1e53274974fa4a4c736a5a3754485a5c56f4947281480773658a";\
        JAVA_URL="https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.4%2B7/OpenJDK21U-jdk_s390x_linux_hotspot_21.0.4_7.tar.gz";\
        ;;\
    amd64)\
        JAVA_SUM="51fb4d03a4429c39d397d3a03a779077159317616550e4e71624c9843083e7b9";\
        JAVA_URL="https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.4%2B7/OpenJDK21U-jdk_x64_linux_hotspot_21.0.4_7.tar.gz";\
        ;;\
    *)\
        printf "Unsupported arch: ${TARGETARCH}";\
        exit 1;\
        ;;\
    esac \
    && apt-get update \
    && apt-get install -y curl unzip \
    && mkdir -p /tmp/openjdk \
    && mkdir -p /tmp/build/openjdk \
    && curl -LfsSo /tmp/build/openjdk.tar.gz "${BUILD_JAVA_URL}" \
    && echo "${BUILD_JAVA_SUM} */tmp/build/openjdk.tar.gz" | sha256sum -c - \
    && curl -LfsSo /tmp/openjdk.tar.gz "${JAVA_URL}" \
    && echo "${JAVA_SUM} */tmp/openjdk.tar.gz" | sha256sum -c - \
    && cd /tmp/build/openjdk \
    && tar -xf /tmp/build/openjdk.tar.gz --strip-components=1 \
    && cd /tmp/openjdk \
    && tar -xf /tmp/openjdk.tar.gz --strip-components=1 \
    && /tmp/build/openjdk/bin/jlink --compress=2 \
           --module-path /tmp/openjdk/jmods/ \
           --add-modules ${JDK_MODULES} \
           --output /opt/openjdk \
    && curl -LfsSo /tmp/fusionauth-app.zip https://files.fusionauth.io/products/fusionauth/${FUSIONAUTH_VERSION}/fusionauth-app-${FUSIONAUTH_VERSION}.zip \
    && mkdir -p /usr/local/fusionauth/fusionauth-app \
    && unzip -nq /tmp/fusionauth-app.zip -d /usr/local/fusionauth

###### Use Ubuntu latest and only copy in what we need to reduce the layer size ###################
FROM ubuntu:noble
RUN apt-get update \
    && apt-get -y install --no-install-recommends curl \
    && apt-get -y upgrade \
    && apt-get -y clean \
    && rm -rf /var/lib/apt/lists \
    && useradd -d /usr/local/fusionauth -U fusionauth
COPY --chown=fusionauth:fusionauth --from=build /opt/openjdk /opt/openjdk
COPY --chown=fusionauth:fusionauth --from=build /usr/local/fusionauth /usr/local/fusionauth

RUN mkdir -p /usr/local/fusionauth/logs \
    && chown fusionauth:fusionauth /usr/local/fusionauth/logs

###### Start FusionAuth App #######################################################################
LABEL description="Create an image running FusionAuth App. Installs FusionAuth App"
LABEL maintainer="FusionAuth <dev@fusionauth.io>"
EXPOSE 9011
USER fusionauth
ENV FUSIONAUTH_USE_GLOBAL_JAVA=1
ENV JAVA_HOME=/opt/openjdk
ENV PATH=$PATH:$JAVA_HOME/bin
CMD ["/usr/local/fusionauth/fusionauth-app/bin/start.sh"]

Here is additional Docker documentation.

Kickstart#

Using Docker with Kickstart is a powerful combination. Using these technologies together lets you:

  • Configure and share development environments
  • Create replicable bug reports
  • Spin up auth instances with a well known, versioned set of data for continuous integration and testing

All the normal limitations of Kickstart apply (the Kickstart will not run if the database has already been set up with an API key, for example).

To use Kickstart, you'll need to tweak your Docker Compose files. Before you begin, you'll need a valid kickstart.json file (the kickstart.json name is just a convention). Check out the Kickstart documentation for more information on writing a kickstart.json.

Once you have a valid kickstart.json file, create a subdirectory in the location of your docker-compose.yml file. It can be named anything; this documentation will use a directory called kickstart. Next, you'll mount this directory and set the FUSIONAUTH_APP_KICKSTART_FILE variable in the docker-compose.yml file.

Here are the steps to do so:

  • In the volumes: section of the fusionauth service, add - ./kickstart:/usr/local/fusionauth/kickstart.
  • Modify .env and add the Kickstart configuration variable: FUSIONAUTH_APP_KICKSTART_FILE=/usr/local/fusionauth/kickstart/kickstart.json. This path should be what the Docker container expects, not the path on the host.
  • Configure docker-compose.yml to pass the environment variable set by .env to the container. Do this by adding FUSIONAUTH_APP_KICKSTART_FILE: ${FUSIONAUTH_APP_KICKSTART_FILE} to the environment section of the fusionauth service.
  • docker compose up -d

The following is an example docker-compose.yml file configuring FusionAuth to run the commands in a kickstart.json at startup.

Example docker-compose.yml for running Kickstart

version: '3'

services:
  db:
    image: postgres:16.0-bookworm
    environment:
      PGDATA: /var/lib/postgresql/data/pgdata
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -U postgres" ]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - db_net
    restart: unless-stopped
    volumes:
      - db_data:/var/lib/postgresql/data

  search:
    image: opensearchproject/opensearch:2.11.0
    environment:
      cluster.name: fusionauth
      discovery.type: single-node
      node.name: search
      plugins.security.disabled: true
      bootstrap.memory_lock: true
      OPENSEARCH_JAVA_OPTS: ${OPENSEARCH_JAVA_OPTS}
    healthcheck:
      interval: 10s
      retries: 80
      test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:9200/
    restart: unless-stopped
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    ports:
      - 9200:9200 # REST API
      - 9600:9600 # Performance Analyzer
    volumes:
      - search_data:/usr/share/opensearch/data
    networks:
      - search_net

  fusionauth:
    image: fusionauth/fusionauth-app:latest
    depends_on:
      db:
        condition: service_healthy
      search:
        condition: service_healthy
    environment:
      DATABASE_URL: jdbc:postgresql://db:5432/fusionauth
      DATABASE_ROOT_USERNAME: ${POSTGRES_USER}
      DATABASE_ROOT_PASSWORD: ${POSTGRES_PASSWORD}
      DATABASE_USERNAME: ${DATABASE_USERNAME}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      FUSIONAUTH_APP_MEMORY: ${FUSIONAUTH_APP_MEMORY}
      FUSIONAUTH_APP_RUNTIME_MODE: ${FUSIONAUTH_APP_RUNTIME_MODE}
      FUSIONAUTH_APP_URL: http://fusionauth:9011
      SEARCH_SERVERS: http://search:9200
      SEARCH_TYPE: elasticsearch
      FUSIONAUTH_APP_KICKSTART_FILE: ${FUSIONAUTH_APP_KICKSTART_FILE}
      FUSIONAUTH_APP_INSTALLATION_SOURCE: fusionauth-example-docker-compose
    networks:
      - db_net
      - search_net
    restart: unless-stopped
    ports:
      - 9011:9011
    volumes:
      - fusionauth_config:/usr/local/fusionauth/config
      - ./kickstart:/usr/local/fusionauth/kickstart

networks:
  db_net:
    driver: bridge
  search_net:
    driver: bridge

volumes:
  db_data:
  fusionauth_config:
  search_data:

After running docker compose up -d you should see a line similar to the one below in the logs. These logs can be accessed using this command: docker compose logs -f fusionauth:

FusionAuth log messages indicating Kickstart has succeeded

io.fusionauth.api.service.system.kickstart.KickstartRunner - Summary

This indicates that Kickstart completed and provides a summary of the configuration changes made by it.

You may also want to check out the Isolated Docker Setups if you want the ability to rapidly stand up different versions and configurations of FusionAuth.

If you want to test changes to your Kickstart file, you'll need to delete your volumes each time. Kickstart won't run except on a brand new install. If there is any data in the database, it won't proceed.

This will delete all data in your docker instance.

Deleting the volumes

docker compose down -v

Plugins#

Instead of building a custom docker image, you can directly mount a directory containing a plugin to your Docker container.

Here are the steps to do so:

  • In the volumes: section of the fusionauth service, add - ./plugins:/usr/local/fusionauth/plugins.
  • Copy your plugin jar file, created by following the instructions, to your plugins directory on the host.
  • docker compose up -d

The following is an example docker-compose.yml file configuring FusionAuth to scan for plugins at startup.

Example docker-compose.yml for installing a plugin

version: '3'

services:
  db:
    image: postgres:16.0-bookworm
    environment:
      PGDATA: /var/lib/postgresql/data/pgdata
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -U postgres" ]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - db_net
    restart: unless-stopped
    volumes:
      - db_data:/var/lib/postgresql/data

  search:
    image: opensearchproject/opensearch:2.11.0
    environment:
      cluster.name: fusionauth
      discovery.type: single-node
      node.name: search
      plugins.security.disabled: true
      bootstrap.memory_lock: true
      OPENSEARCH_JAVA_OPTS: ${OPENSEARCH_JAVA_OPTS}
    healthcheck:
      interval: 10s
      retries: 80
      test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:9200/
    restart: unless-stopped
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    ports:
      - 9200:9200 # REST API
      - 9600:9600 # Performance Analyzer
    volumes:
      - search_data:/usr/share/opensearch/data
    networks:
      - search_net

  fusionauth:
    image: fusionauth/fusionauth-app:latest
    depends_on:
      db:
        condition: service_healthy
      search:
        condition: service_healthy
      maven:
        condition: service_completed_successfully
    environment:
      DATABASE_URL: jdbc:postgresql://db:5432/fusionauth
      DATABASE_ROOT_USERNAME: ${POSTGRES_USER}
      DATABASE_ROOT_PASSWORD: ${POSTGRES_PASSWORD}
      DATABASE_USERNAME: ${DATABASE_USERNAME}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      FUSIONAUTH_APP_MEMORY: ${FUSIONAUTH_APP_MEMORY}
      FUSIONAUTH_APP_RUNTIME_MODE: ${FUSIONAUTH_APP_RUNTIME_MODE}
      FUSIONAUTH_APP_URL: http://fusionauth:9011
      SEARCH_SERVERS: http://search:9200
      SEARCH_TYPE: elasticsearch
      FUSIONAUTH_APP_KICKSTART_FILE: ${FUSIONAUTH_APP_KICKSTART_FILE}
      FUSIONAUTH_APP_INSTALLATION_SOURCE: fusionauth-example-docker-compose
    networks:
      - db_net
      - search_net
    restart: unless-stopped
    ports:
      - 9011:9011
    volumes:
      - fusionauth_config:/usr/local/fusionauth/config
      - ./kickstart:/usr/local/fusionauth/kickstart
      - ./plugins:/usr/local/fusionauth/plugins

  maven:
    image: maven
    command:
      - /bin/sh
      - -c
      - |
        git init .
        git remote add -t \* -f origin https://github.com/FusionAuth/fusionauth-example-password-encryptor.git
        git checkout master
        mvn compile package
        cp target/fusionauth-example-password-encryptor*.jar output/
        chown 1000:1000 output/*
    volumes:
      - maven_cache:/root/.m2
      - plugin_source:/app
      - ./plugins:/app/output
    working_dir: /app

networks:
  db_net:
    driver: bridge
  search_net:
    driver: bridge

volumes:
  db_data:
  fusionauth_config:
  search_data:
  maven_cache:
  plugin_source:

After running docker compose up -d you should see a line like this in the logs, which you can access using the command docker compose logs -f fusionauth:

FusionAuth log messages indicating a plugin has been successfully installed

INFO  io.fusionauth.api.plugin.guice.PluginModule - Installing plugin [com.mycompany.fusionauth.plugins.guice.MyExampleFusionAuthPluginModule]
INFO  io.fusionauth.api.plugin.guice.PluginModule - Plugin successfully installed

Such output indicates that the plugin has been installed and can be used.

Accessing the Host Machine#

The default FusionAuth Docker configuration sets up the network using the bridge configuration. This means that all the hosts defined by docker-compose.yml can access each other. However, it means that any applications running on your host machine cannot be accessed by FusionAuth using localhost.

This is typically only an issue when FusionAuth is accessing resources outside of the Docker network to, for example, send email or request a webhook. For example, if an application is running locally and you want FusionAuth, running in Docker, to send a webhook payload to it, configuring FusionAuth to send the webhook to localhost won't work. localhost in the Docker container refers to the Docker container itself, not the host machine.

In this situation, do one of the following:

  • Run a container with your application in Docker, and use the appropriate network domain name.
  • Install FusionAuth on the host machine and use localhost.
  • Use an alias address, host.docker.internal, as the hostname instead of localhost.
    • For macOS and Windows hosts, you can use host.docker.internal without any additional configuration.
    • For Linux hosts, add the following lines to your docker-compose.yml.
extra_hosts: 
  - "host.docker.internal:host-gateway"

Modifying the FusionAuth service in docker-compose.yml to use other Docker networking schemes such as host may work, but isn't fully tested or supported.

OpenSearch Production Deployment Configuration#

Production runtime requirements and configuration for OpenSearch will drastically differ from the Docker Compose examples as the examples are configured without any security or redundancy.

Please review the Installing OpenSearch documentation for further details around production deployment.

Elasticsearch Production Deployment Configuration#

Elasticsearch has a few runtime requirements that may not be met by default on your host platform. Please review the Elasticsearch Docker production mode guide for more information.

For example if startup is failing and you see the following in the logs, you will need to increase vm.max_map_count on your host VM.

The log message when max_map_count is too low

2018-11-22T12:32:06.779828954Z Nov 22, 2018 12:32:06.779 PM ERROR c.inversoft.maintenance.search.ElasticsearchSilentConfigurationWorkflowTask
  - Silent configuration was unable to complete search configuration. Entering maintenance mode. State [SERVER_DOWN]

2018-11-22T13:00:05.346558595Z ERROR: [2] bootstrap checks failed
2018-11-22T13:00:05.346600195Z [1]: memory locking requested for elasticsearch process but memory is not locked
2018-11-22T13:00:05.346606495Z [2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

Mailcatcher#

In development environments, consider running MailCatcher, which provides a local SMTP server. This allows FusionAuth to send transactional emails for account verification, password reset, and more.

Example docker-compose.yml with mailcatcher

version: '3'

services:
  db:
    image: postgres:16.0-bookworm
    environment:
      PGDATA: /var/lib/postgresql/data/pgdata
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -U postgres" ]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - db_net
    restart: unless-stopped
    volumes:
      - db_data:/var/lib/postgresql/data

  search:
    image: opensearchproject/opensearch:2.11.0
    environment:
      cluster.name: fusionauth
      discovery.type: single-node
      node.name: search
      plugins.security.disabled: true
      bootstrap.memory_lock: true
      OPENSEARCH_JAVA_OPTS: ${OPENSEARCH_JAVA_OPTS}
    healthcheck:
      interval: 10s
      retries: 80
      test: curl --write-out 'HTTP %{http_code}' --fail --silent --output /dev/null http://localhost:9200/
    restart: unless-stopped
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    ports:
      - 9200:9200 # REST API
      - 9600:9600 # Performance Analyzer
    volumes:
      - search_data:/usr/share/opensearch/data
    networks:
      - search_net

  mailcatcher:
    image: sj26/mailcatcher
    ports:
      - "1025:1025"
      - "1080:1080"
    healthcheck:
      interval: 10s
      retries: 80
      test: wget -q -O /dev/null http://mailcatcher:1080/
    networks:
      - mailcatcher_net

  fusionauth:
    image: fusionauth/fusionauth-app:latest
    depends_on:
      db:
        condition: service_healthy
      search:
        condition: service_healthy
      mailcatcher:
        condition: service_healthy
    environment:
      DATABASE_URL: jdbc:postgresql://db:5432/fusionauth
      DATABASE_ROOT_USERNAME: ${POSTGRES_USER}
      DATABASE_ROOT_PASSWORD: ${POSTGRES_PASSWORD}
      DATABASE_USERNAME: ${DATABASE_USERNAME}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      FUSIONAUTH_APP_MEMORY: ${FUSIONAUTH_APP_MEMORY}
      FUSIONAUTH_APP_RUNTIME_MODE: ${FUSIONAUTH_APP_RUNTIME_MODE}
      FUSIONAUTH_APP_URL: http://fusionauth:9011
      SEARCH_SERVERS: http://search:9200
      SEARCH_TYPE: elasticsearch
      FUSIONAUTH_APP_KICKSTART_FILE: ${FUSIONAUTH_APP_KICKSTART_FILE}
      FUSIONAUTH_APP_INSTALLATION_SOURCE: fusionauth-example-docker-compose
    networks:
      - db_net
      - search_net
      - mailcatcher_net
    restart: unless-stopped
    ports:
      - 9011:9011
    volumes:
      - fusionauth_config:/usr/local/fusionauth/config
      - ./kickstart:/usr/local/fusionauth/kickstart

networks:
  db_net:
    driver: bridge
  search_net:
    driver: bridge
  mailcatcher_net:
    driver: bridge

volumes:
  db_data:
  fusionauth_config:
  search_data:

Below is a functional configuration on the tenant email tab based on the above configuration file:

Tenant Setting With MailCatcher

This a view of the Mailcatcher client.

Mailcatcher Client View

Limitations#

Due to Oracle licensing restrictions, the docker images published on Docker Hub do not contain the necessary software to connect to a MySQL database.

If you wish to use MySQL, you'll need to build a custom container that includes the MySQL Connector JAR file. Here is an example container definition that uses the FusionAuth image as a base layer and adds the MySQL connector.

Example Dockerfile which downloads the MySQL connector

#
# FusionAuth App Dockerfile including the MySQL connector
#
# Note:
# -----------------------------------------------------------------------------
# The MySQL JDBC connector is not bundled with FusionAuth due to the GPL
# license terms under which Oracle publishes this software.
#
# Because of this restriction, you will need to build a docker image for your
# use that contains the MySQL JDBC connector in order to connect to a MySQL
# database at runtime.

# Source: https://github.com/mysql/mysql-connector-j
# License: https://github.com/mysql/mysql-connector-j/blob/release/8.0/LICENSE
# Homepage: https://dev.mysql.com/doc/connector-j/8.0/en/
#
# If you choose to build a Docker image containing this connector, ensure you
# are aware and in compliance with the license under which the MySQL JDBC connector
# is provided.
#
# This file is provided as an example only.
# -----------------------------------------------------------------------------
#
# Build:
#   > docker build -t fusionauth/fusionauth-app-mysql:1.64.1 .
#   > docker build -t fusionauth/fusionauth-app-mysql:latest .
#
# Run:
#  > docker run -p 9011:9011 -it fusionauth/fusionauth-app-mysql
#

FROM fusionauth/fusionauth-app:1.64.1
ADD --chown=fusionauth:fusionauth https://search.maven.org/remotecontent?filepath=com/mysql/mysql-connector-j/9.0.0/mysql-connector-j-9.0.0.jar /usr/local/fusionauth/fusionauth-app/lib