What you will learn
- How to run RavenDB with HTTPS certificate on Docker
- How to generate self-signed certificates
Introduction
Running applications in Docker containers offers numerous advantages over installing them directly on a system. Docker ensures isolation and consistency by encapsulating an application and its dependencies, preventing conflicts, and guaranteeing the same behavior across different environments. Its portability allows containers to run on any system that has Linux kernel to be shared, simplifying the movement of applications between environments and eliminating dependency conflicts.
RavenDB provides official Docker images that can be pulled from Docker Hub. These images support both Linux and Windows containers, ensuring broad compatibility across different systems. The images are available in two repositories:
- https://hub.docker.com/r/ravendb/ravendb – contains images of stable releases.
- https://hub.docker.com/r/ravendb/ravendb-nightly – contains images of nightly development builds.
These Docker images support a variety of architectures, including:
- Linux: arm32, arm64, and amd64
- Windows: amd64
In this tutorial, we are going to use the multi-platform image ravendb/ravendb:latest
.
Prerequisites
Before running RavenDB we need to prepare an environment. First of all we need a place to keep the data for the RavenDB server.
mkdir -p ~/ravendb/data
In order to have a secure connection with the server it’s recommended to use HTTPS protocol. To do that we will need a DNS entry pointing to the server and server certificate. It’s best to use automated tools like certbot
for certificate generation and for example AWS Route 53 as DNS service.
For the purpose of this tutorial, to simplify the setup, we will use a self-generated certificate and entry in /etc/hosts
. Using self-signed certificates is not recommended for public-facing websites and services. Those are not trusted by default by the OS and must be added as root in the certificate store on every server and client machines that want to communicate. Recommended approach is to use certificates from a certificate authority like Let’s Encrypt, ZeroSSL or any other well-known certificate issuer. There is also an option to use RavenDB initial setup to get a certificate.
mkdir -p ~/ravendb/certs
cd ~/ravendb/certs
Specify URL for the instance (this creates an environment variable that we can use later):
RAVENDB_URL="my-test.instance"
Generate private key and server certificate:
openssl req -x509 -newkey rsa:4096 -sha256 \
-days 3650 -nodes -keyout my-test.instance.key \
-out my-test.instance.crt -subj "/CN=$RAVENDB_URL" \
-extensions v3_req -config <(printf
"[v3_req]\nkeyUsage=digitalSignature,keyEncipherment\nextendedKeyUsage=serverAuth,clientAuth\nsubjectAltName=DNS:$RAVENDB_URL")
Generate PFX file from private key and server certificate:
openssl pkcs12 -export -out my-test.instance.pfx -inkey my-test.instance.key -in my-test.instance.crt
Remember to save the PFX certificate on your local machine and install it in the root certificate store. You will need it to connect to the RavenDB server for the first time.
When running applications inside Docker containers, like RavenDB, it’s common to manage file permissions on the host system that need to be accessible to the container. In Linux, every user and group is associated with a unique identifier known as a User ID (UID) and a Group ID (GID), respectively. These numerical identifiers are crucial for managing file ownership and permissions. The container’s processes may run under a user with a specific UID and GID, which can differ from those on the host system. In the case of RavenDB, it uses 999 as both the UID and GID for its internal user. This means files on the host system that are mounted into the container must be accessible by the user with these identifiers. Without this adjustment, the container might not have the necessary permissions to read or write to the mounted files, leading to potential errors or malfunctioning of the application.
In summary, while human-readable names are convenient on a single system, UIDs and GIDs ensure consistency across different environments, especially in containerized applications like those running in Docker. Therefore, using numerical identifiers is essential when managing permissions between the host and the Docker container to ensure seamless operation.
Here is the command to change file permissions:
sudo chown -R 999:999 ~/ravendb/
The last step is to obtain a license from https://ravendb.net/buy#developer. There is a free developer license available.
Setup
With the environment prepared we can start building our docker compose. To keep everything in one place let’s create docker-compose.yml
file in ~/ravendb
directory.
touch ~/ravendb/docker-compose.yml
We can start writing docker compose file with our favorite editor. It starts the same as always with specifying services, naming containers, and selecting Docker image. As mentioned before we will be using RavenDB’s multi-platform image:
version: '3'
services:
ravendb:
container_name: ravendb
image: ravendb/ravendb:latest
Now we can mount the directories prepared earlier. RavenDB by default keeps the data in /var/lib/ravendb/data
. The folder with certificate can be mounted anywhere within the container. We will specify a path to it in the next step. Replace the {username}
with your linux username.
volumes:
- type: bind
source: /home/{username}/ravendb/data
target: /var/lib/ravendb/data
- type: bind
source: /home/{username}/ravendb/certs
target: /certs
We would like to be able to access the server. Port binding will help us with that. I’m going to use the default port for the HTTPS protocol. There is also a second port needed for inter-node communication (replication) or data subscription processing. Default used by RavenDB is 38888 so that’s what I’m going to use here as well.
ports:
- 443:443
- 38888:38888
Based on values set in previous steps we can configure RavenDB server with environment variables. Several key environment variables ensure that the server runs correctly and securely. Here’s an overview of these options and what they do:
RAVEN_Setup_Mode=None
Disables the setup wizard. By setting this to None, RavenDB assumes all necessary configurations are provided through environment variables or configuration files, which is ideal for automated deployments.
RAVEN_License_Eula_Accepted=true
Accepts the End User License Agreement (EULA) for RavenDB. Setting this to true is necessary to run the server without manual intervention, streamlining automated deployment processes.
RAVEN_ServerUrl=https://0.0.0.0
Specifies what URL RavenDB will bind to for listening on the network. The 0.0.0.0
address is a wildcard that allows the server to accept connections from any network interface.
RAVEN_PublicServerUrl=https://my-test.instance
Sets the public URL for the RavenDB server. This is the address clients use to connect to the server. Replace https://my-test.instance
with the actual domain accessible to clients.
RAVEN_ServerUrl_Tcp=tcp://0.0.0.0:38888
Configures the server to listen on all network interfaces for TCP connections on port 38888
. The 0.0.0.0
address allows connections from any network interface.
RAVEN_PublicServerUrl_Tcp=tcp://my-test.instance:38888
Sets the public TCP URL for the RavenDB server, used by clients for TCP communication. Replace tcp://my-test.instance:38888
with the actual public address and port number.
RAVEN_Security.Certificate.Path=/certs/my-test.instance.pfx
Specifies the path to the SSL certificate file used to secure connections to the RavenDB server. The certificate should be in .pfx format and include both the certificate and private key, securing HTTPS and TCP connections.
There are other options to pass certificates to RavenDB, especially useful in containerization scenarios, where the certificate comes from and is renewed by an external facility. You might want to use the approach described here.
Also you might have to recreate the container on certificate renewal, unless you configured the renewal process with RAVEN_Security.Certificate.Renew.Exec
environment variable.
RAVEN_License=’{Put your license here}’
Specifies the RavenDB license string. This is required to activate the license for the RavenDB server, enabling full functionality and compliance with the licensing terms.
These environment variables allow for precise control over the RavenDB server’s network settings, security configurations, and deployment behavior, making it adaptable for various deployment scenarios. For more detailed guidance, refer to the RavenDB documentation: https://ravendb.net/docs/article-page/latest/csharp/server/configuration/configuration-options
environment:
- RAVEN_Setup_Mode=None
- RAVEN_License_Eula_Accepted=true
- RAVEN_ServerUrl=https://0.0.0.0
- RAVEN_PublicServerUrl=https://my-test.instance
- RAVEN_ServerUrl_Tcp=tcp://0.0.0.0:388888
- RAVEN_PublicServerUrl_Tcp=tcp://my-test.instance:38888
- RAVEN_Security.Certificate.Path=/certs/my-test.instance.pfx
In my case there is one more step required. Because I don’t have any public DNS entry RavenDB server won’t be able to resolve the hostname I used. Therefore, I have to tell docker that my-test.instance is his own address:
extra_hosts:
- "my-test.instance:127.0.0.1"
Final docker-compose.yml
The final version of docker-compose.yml
should look like this:
version: '3'
services:
ravendb:
container_name: ravendb
image: ravendb/ravendb:latest
volumes:
- type: bind
source: /home/{username}/ravendb/data
target: /var/lib/ravendb/data
- type: bind
source: /home/{username}/ravendb/certs
target: /certs
ports:
- 443:443
- 38888:38888
environment:
- RAVEN_Setup_Mode=None
- RAVEN_License_Eula_Accepted=true
- RAVEN_ServerUrl=https://0.0.0.0
- RAVEN_PublicServerUrl=https://my-test.instance
- RAVEN_ServerUrl_Tcp=tcp://0.0.0.0:388888
- RAVEN_PublicServerUrl_Tcp=tcp://my-test.instance:38888
- RAVEN_Security.Certificate.Path=/certs/my-test.instance.pfx
- RAVEN_License=’{Put your license here}’
extra_hosts:
- "my-test.instance:127.0.0.1"
The only thing left to do is to start the container:
docker compose -f ~/ravendb/docker-compose.yml up -d --force-recreate --wait
Validation
We can now check if the container is running:
docker compose ls
The output should look like this:
$ docker compose ls
NAME STATUS CONFIG FILES
ravendb running(1) ~/ravendb/docker-compose.yml
Let’s try connecting to the RavenDB server using the URL and certificate that were both created in the prerequisites. In my case it’s https://my-test.instance
You can now set up your node.
Summary
In this tutorial we provided a guide on setting up RavenDB with HTTPS in a Docker environment. We outlined the process of creating necessary directories, generating self-signed SSL certificates using OpenSSL, configuring RavenDB via a Docker Compose file, and ensuring proper file permissions. While the tutorial uses self-signed certificates for demonstration purposes, it’s recommended to obtain SSL certificates from trusted Certificate Authorities like Let’s Encrypt or ZeroSSL for production environments to ensure enhanced security and trustworthiness.