Keycloak production deployment with MariaDB, NGINX and Docker

This post will cover the detailed step by step process to deploy Keycloak in a production environment using AWS EC2 Server. We will use Docker containers and Maria DB as the database. For securing the portal, we will use NGINX and certbot to generate Letsencrypt certificate.

The steps can be broken down as following:

  1. Create a domain/subdomain to be used for Keycloak
  2. Create the EC2 Server in AWS with the correct inbound rule set
  3. Install NGINX and Certbot and configure it for Keycloak domain
  4. Install Docker and configure it to run without “sudo”
  5. Create docker-compose.yml file
  6. Bring up the container and validate everything

1. Create a Domain or Subdomain to be used for Keycloak

I have created an Elastic IP in AWS to be used for Keycloak. We are using a subdomain for an existing domain by creating an “A” record. Please refer to the following image for reference.

2. Create the EC2 Server in AWS Console

Login to your AWS account and create an Amazon EC2 Server. Please make sure to size it based on the expected load. The recommended minimum hardware is 2 core CPU & 4 GB RAM which we will use for this deployment.

Please make sure to allow inbound TCP ports 22, 443 and 8081 that we will be using in this deployment. Next map the Elastic ip address to the newly created Keycloak Server. Once the server is up, perform the package upgrade using following command

sudo apt update && sudo apt upgrade

sudo apt update && sudo apt upgrade

Go ahead and reboot the server as required.

3. Install NGINX and configure it for Keycloak domain

Now we will install the NGINX

$ sudo apt install nginx

Type Y when prompted. Once the installation is completed, validate the NGINX Version using following command

$ nginx -v
nginx version: nginx/1.18.0 (ubuntu)
$ sudo systemctl status nginx

Last command should give you output like the following, which confirms that our NGINX is up and running

Next we will enable and configure the UFW to allow NGINX and ssh for remote connectivity.

$ sudo ufw status
Status: inactive
$ sudo ufw allow "Nginx Full"
Rules updated
Rules updated (v6)
$ sudo ufw allow ssh
Rules updated
Rules updated (v6)
$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
$ sudo ufw reload
Firewall reloaded
$ sudo ufw status
Status: active

To                          Action      From
--                          ------      ----
Nginx Full                  ALLOW       Anywhere
22/tcp                      ALLOW       Anywhere
Nginx Full (v6)             ALLOW       Anywhere (v6)
22/tcp (v6)                 ALLOW       Anywhere (v6)

Next we will install certbot for Letsencrypt certificate creation and automated renewal

$ sudo apt install certbot python3-certbot-nginx

Reading package lists... Done
Building dependency tree... Done
Reading state information... Done

The following additional packages will be installed:
  python3-acme python3-certbot python3-configargparse python3-icu python3-josepy
  python3-parsedatetime python3-requests-toolbelt python3-rfc3339
  python3-zope.component python3-zope.event python3-zope.hookable

Suggested packages:
  python-certbot-doc python3-certbot-apache python-acme-doc
  python-certbot-nginx-doc

The following NEW packages will be installed:
  certbot python3-acme python3-certbot python3-certbot-nginx
  python3-configargparse python3-icu python3-josepy python3-parsedatetime
  python3-requests-toolbelt python3-rfc3339 python3-zope.component
  python3-zope.event python3-zope.hookable

0 upgraded, 13 newly installed, 0 to remove and 0 not upgraded.
Need to get 993 kB of archives.
After this operation, 5077 kB of additional disk space will be used.
Do you want to continue? [Y/n] Y

Click on Y to continue. Once the installation is successfully completed, next step will be to create out certificate. Run the following commands and provide all the required information.

$ sudo certbot --nginx -d keycloak.securityfocal.com -d www.keycloak.securityfocal.com

Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You must agree in
order to register with the ACME server. Do you agree?
(Y)es/(N)o: Y

Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
(Y)es/(N)o: Y

Account registered.

Requesting a certificate for keycloak.securityfocal.com and www.keycloak.securityfocal.com

Successfully received certificate.

Certificate is saved at: /etc/letsencrypt/live/keycloak.securityfocal.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/keycloak.securityfocal.com/privkey.pem
This certificate expires on 2025-02-28.

These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

Deploying certificate

Successfully deployed certificate for keycloak.securityfocal.com to /etc/nginx/sites-enabled/default
Successfully deployed certificate for www.keycloak.securityfocal.com to /etc/nginx/sites-enabled/default

Congratulations! You have successfully enabled HTTPS on https://keycloak.securityfocal.com
and https://www.keycloak.securityfocal.com

If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le

sudo certbot renew --dry-run

Next we will do a dry run to validate if auto renewal is working as expected

$ sudo certbot renew --dry-run

Saving debug log to /var/log/letsencrypt/letsencrypt.log

Account registered.
Simulating renewal of an existing certificate for keycloak.securityfocal.com and www.keycloak.securityfocal.com

Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/keycloak.securityfocal.com/fullchain.pem (success)

Next we will configure NGINX for our deployment.

$ sudo unlink /etc/nginx/sites-enabled/default
$ cd /etc/nginx/sites-available/
$ sudo vi keycloak.securityfocal.conf

We will use following configuration for our Keycloak Deployment. Please make sure to change the configuration parameters as per your environment.

server {
    listen 80;
    server_name keycloak.securityfocal.com www.keycloak.securityfocal.com;
    rewrite ^https://keycloak.securityfocal.com permanent;
}

server {
    listen 443 ssl;
    server_name keycloak.securityfocal.com www.keycloak.securityfocal.com;

    ssl_certificate /etc/letsencrypt/live/keycloak.securityfocal.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/keycloak.securityfocal.com/privkey.pem;
    ssl_session_cache builtin:1000 shared:ssl:10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_set_header     Host               $host;
        proxy_set_header     X-Real-IP          $remote_addr;
        proxy_set_header     X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header     X-Forwarded-Proto  $scheme;
        proxy_pass http://65.0.171.3:8081;
    }
}

Save and close the file (:wq). Complete the NGINX configuration

$ sudo ln -s /etc/nginx/sites-available/keycloak.securityfocal.conf /etc/nginx/sites-enabled/keycloak.securityfocal.conf
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl restart nginx

Next we will install Docker and Docker compose. So to do that we will set up Docker’s apt repository.

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

4. Install Docker and configure it to run without “sudo”

$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verify that the installation is successful by running the hello-world image:

$ sudo docker run hello-world

This command downloads a test image and runs it in a container. When the container runs, it prints a confirmation message and exits.

To run Docker without root privileges, use the following commands

$ sudo groupadd docker

$ sudo usermod -aG docker $USER

Log out and log back in so that your group membership is re-evaluated.

5. Create docker-compose.yml file

We now need to create our docker-compose file. You may use the following code for that, please make sure to change the key parameters:

$ sudo vi docker-compose.yml

services:
  mariadb:
    image: mariadb:latest
    environment:
      MARIADB_ROOT_PASSWORD: toor
      MARIADB_DATABASE: keycloak
      MARIADB_USER: keycloak
      MARIADB_PASSWORD: keycloak
      mem_limit: 300m
    container_name: mariadb
    restart: always
    volumes:
      - mariadb_data:/var/lib/mysql
    networks:
      - keycloak-auth

  keycloak:
    image: quay.io/keycloak/keycloak:latest
    environment:
      KC_DB: mariadb
      KC_DB_SCHEMA: keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: keycloak
      KC_DB_URL_HOST: mariadb
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
      KC_PROXY_HEADERS: xforwarded
      KC_HTTP_ENABLED: true
      KC_HOSTNAME_STRICT: false
      KC_HOSTNAME_URL: https://mydomain
      mem_limit: 500m
    ports:
      - 8443:8443
      - 8081:8080
    container_name: keycloak
    restart: always
    networks:
      - keycloak-auth
    depends_on:
      - mariadb
    command: start
volumes:
  mariadb_data:
    driver: local
networks:
  keycloak-auth:
    driver: bridge

You can use following github repository also for reference: docker-compose.yml .

6. Bring up the container and validate everything

$ docker-compose up

If you have followed all the steps correctly, the Keycloak Server should come up

That’s it our server is ready. Use the admin credentials to login.