
[[_external_database]]
=== Connecting to an external database

You can use the Operator to connect to an external PostgreSQL database by creating a `keycloak-db-secret` YAML file and setting Keycloak CR externalDatabase property to enabled. Note that values are Base64 encoded.

.Example YAML file for `keycloak-db-secret`
```yaml
apiVersion: v1
kind: Secret
metadata:
    name: keycloak-db-secret
    namespace: keycloak
stringData:
    POSTGRES_DATABASE: <Database Name>
    POSTGRES_EXTERNAL_ADDRESS: <External Database IP or URL (resolvable by K8s)>
    POSTGRES_EXTERNAL_PORT: <External Database Port>
    POSTGRES_PASSWORD: <Database Password>
    # Required for AWS Backup functionality
    POSTGRES_SUPERUSER: "true"
    POSTGRES_USERNAME: <Database Username>
    SSLMODE: <TLS configuration for the Database connection>
type: Opaque
```

The following properties set the hostname or IP address and port of the database.

* `POSTGRES_EXTERNAL_ADDRESS` - an IP address or a hostname of the external database.
ifeval::[{project_community}==true]
This address needs be resolvable in a Kubernetes cluster.
endif::[]
* `POSTGRES_EXTERNAL_PORT` - (Optional) A database port.

The other properties work in the same way for a hosted or external database. Set them as follows:

* `POSTGRES_DATABASE` - Database name to be used.
* `POSTGRES_USERNAME` - Database username
* `POSTGRES_PASSWORD` - Database password
* `POSTGRES_SUPERUSER` - Indicates whether backups should run as super user. Typically `true`.
* `SSL_MODE` - Indicates whether to use TLS on the connection to the external PostgreSQL database. Check the possible https://www.postgresql.org/docs/current/libpq-ssl.html[values]

When `SSL_MODE` is enabled, the operator searches for a secret called `keycloak-db-ssl-cert-secret` containing the `root.crt` that has been used by the PostgreSQL database. Creating the secret is optional and the secret is used only when you want to verify the Database's certificate (for example `SSLMODE: verify-ca`). Here is an example :

.Example YAML file for `TLS Secret` to be used by the operator.
```yaml
apiVersion: v1
kind: Secret
metadata:
  name: keycloak-db-ssl-cert-secret
  namespace: keycloak
type: Opaque
data:
  root.crt: {root.crt base64}
```

The Operator will create a Service named `keycloak-postgresql`. This Service is configured by the Operator to expose the external database based on the content of `POSTGRES_EXTERNAL_ADDRESS`. {project_name} uses this Service to connect to the Database, which means it does not connect to the Database directly but rather through this Service.

The Keycloak custom resource requires updates to enable external database support. 

.Example YAML file for `Keycloak` custom resource that supports an external database
```yaml
apiVersion: keycloak.org/v1alpha1
kind: Keycloak
metadata:
  labels:
ifeval::[{project_community}==true]
      app: example-keycloak
endif::[]  
ifeval::[{project_product}==true]
      app: sso
endif::[]  
  name: example-keycloak
  namespace: keycloak
spec:
  externalDatabase:
    enabled: true
  instances: 1
```

.Prerequisites

* You have a YAML file for `keycloak-db-secret`.
* You have modified the Keycloak custom resource to set `externalDatabase` to `true`.
* You have cluster-admin permission or an equivalent level of permissions granted by an administrator.

.Procedure

. Locate the secret for your PostgreSQL database: `{create_cmd_brief} get secret <secret_for_db> -o yaml`. For example:
+
[source,bash,subs=+attributes]
----
$ {create_cmd_brief} get secret keycloak-db-secret -o yaml
apiVersion: v1
data
  POSTGRES_DATABASE: cm9vdA==
  POSTGRES_EXTERNAL_ADDRESS: MTcyLjE3LjAuMw==
  POSTGRES_EXTERNAL_PORT: NTQzMg==
----
+
The `POSTGRES_EXTERNAL_ADDRESS` is in Base64 format.

. Decode the value for the secret: `echo "<encoded_secret>" | base64 -decode`. For example:
+
[source,bash,subs=+attributes]
----
$ echo "MTcyLjE3LjAuMw==" | base64 -decode
192.0.2.3
----

. Confirm that the decoded value matches the IP address for your database:
+
[source,bash,subs=+attributes]
----
$ {create_cmd_brief} get pods -o wide
NAME                        READY  STATUS    RESTARTS   AGE   IP
keycloak-0                  1/1    Running   0          13m   192.0.2.0
keycloak-postgresql-c8vv27m 1/1    Running   0          24m   192.0.2.3
----

. Confirm that `keycloak-postgresql` appears in a list of running services:
+
[source,bash,subs=+attributes]
----
$ {create_cmd_brief} get svc
NAME                 TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)   AGE
keycloak             ClusterIP  203.0.113.0    <none>       8443/TCP  27m
keycloak-discovery   ClusterIP  None           <none>       8080/TCP  27m
keycloak-postgresql  ClusterIP  203.0.113.1    <none>       5432/TCP  27m
----
+
The `keycloak-postgresql` service sends requests to a set of IP addresses in the backend.  These IP addresses are called endpoints.

. View the endpoints used by the `keycloak-postgresql` service to confirm that they use the IP addresses for your database:
+
[source,bash,subs=+attributes]
----
$ {create_cmd_brief} get endpoints keycloak-postgresql
NAME                  ENDPOINTS         AGE
keycloak-postgresql   192.0.2.3.5432    27m
----

. Confirm that {project_name} is running with the external database. This example shows that everything is running:
+
[source,bash,subs=+attributes]
----
$ {create_cmd_brief} get pods
NAME                        READY  STATUS    RESTARTS   AGE   IP
keycloak-0                  1/1    Running   0          26m   192.0.2.0
keycloak-postgresql-c8vv27m 1/1    Running   0          36m   192.0.2.3
----

ifeval::[{project_community}==true]
.Additional Resources

* To back up your database using custom resources, see xref:_backup-cr[Scheduling database backups].


* For more information on Base64 encoding, see the https://kubernetes.io/docs/concepts/configuration/secret/[Kubernetes Secrets manual].
endif::[]
