If you want to do some development against Snomed CT terminology server, you can download Snowstorm, an open source terminogy server with support for Snomed CT.
It is primarily written in Java and built on top of ElasticSerach.
The easiest way of getting Snowstorm running in your local development server (or on an actual server if desired), is to download the Docker images for Elasticsearch and Snowstorm.
I have added a Yaml file on my Github repo for installing both Elasticsearch and Snowstorm here:
docker-compose.yml
The Yaml file sets up the following for Elastsearch and Snowstorm :
Elasticsearch
- Sets up Elasticsearch as a service that Snowstorm depends upon on port 9200
- Pulls Elasticsearch version 8.19.8. You obviously want to test out even newer versions of Elasticsearch later on
- Sets up a durable volume for Elasticseachon elastic:/usr/share/elasticsearch/data
- Snomed depends upon Elasticsearch and the condition must be healthy of Elasticsearch from the healthchecks
- Memory reservation is set to 4 GB for ElasticSearch
- Since in this example Elasticsearch is set up to run in LocalDEV, xpack.security.enabled is set to false. This turns off authorization, authentication, TLS and audit since the example setup in this docker compose file is meant for development environments, not for production.
Snowstorm
- Sets up Snowstorm to use Elasticsearch as a service on port 9200
- Snowstorm is set up to run on 8080 (HTTP)
- A bind mount is set up on ./rf2 that is on the host computer and mapped to the ./rf2 folder in the container. We use this bind mount to import the Rf2 file later on
docker-compose.yml
# https://github.com/IHTSDO/snowstorm/blob/master/docker-compose.yml
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.19.8
container_name: elasticsearch
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- node.name=snowstorm
- cluster.name=snowstorm-cluster
- "ES_JAVA_OPTS=-Xms4g -Xmx4g"
volumes:
- elastic:/usr/share/elasticsearch/data
networks:
elastic:
aliases:
- es
healthcheck:
test: ["CMD", "curl", "-f", "http://es:9200"]
interval: 1s
timeout: 1s
retries: 60
ports:
- 127.0.0.1:9200:9200
mem_reservation: 4g
snowstorm:
image: snomedinternational/snowstorm:latest
depends_on:
elasticsearch:
condition: service_healthy
environment:
- elasticsearch.urls=http://elasticsearch:9200
- JAVA_TOOL_OPTIONS=-Xms2g -Xmx4g --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED
command: [
"--elasticsearch.urls=http://es:9200",
]
networks:
elastic:
aliases:
- snowstorm
ports:
- "8080:8080"
volumes:
- ./rf2:/rf2
networks:
elastic:
name: elastic
volumes:
elastic:
A component diagram (UML) of the Rf2 format is shown below. Note that RefsetItem is extensible and contain additional fields depending on the refset type.
Obtaining Rf2 files with Snomed Ct for Snowstorm
One way of obtaining Snomed Ct Rf2 files is to head over to National Clinical Terminology Service (NCTS) in Australia. They offer individual access to Snomed CT Rt2 files after a quick registration.The Digital Health Implementer Hub has a Native Snomed Ct Rf2 file page here. Note that this comes with the Australian Snomed CT-AU bundle, but includes the International core terminology data set. https://implementer.digitalhealth.gov.au/ncts/native-snomed-ct-rf2-files This page has an entity relationship diagram shown below for the Rf2 data:
One tip concerning uploading a backup of the Rf2 file to Github is to use 7-zip and split up the volume into multiple files. You can upload 100 MB in maximum file sizes to Github. The Snomed Ct-AU file is about 890 MB as of November 2026.
When you have the Rf2 files uploaded to the folder Rf2, a subdir of where you started the docker compose yaml file, make sure the Rf2 folder contains the .Zip file with the Rf2 file containing the Snomed CT file to import into the Snowstorm service running with Elasticsearch.
Starting up Snowstorm
Navigate to the folder where we have the docker-compose.yml file and just start up the Docker compose file. Start up Docker for Desktop to be sure Docker service is running.
Please note that when doing the import, it can be easier to view all the logs if running the docker command from the command line. You can see the Logs inside Docker Desktop, but the commandline will be more verbose.
As noted, make sure you have the Rf2 subfolder. The bat file below contains a small script that will copy the extracted ALL zip file (you will have to obtain the Rf2 file yourself due to the fact it requires registration), so adjust the folder and file.
CopySnomedInternationalRf2.bat
mkdir rf2
cd rf2
xcopy C:\src\Snowstorm.Snowmed.Rf2_International_Distribution\NCTS_SCT_RF2_DISTRIBUTION_32506021000036107-20261031-ALL.zip .
To get Snowstorm up and running with Elasticsearch, just run this command (note that the first time, Docker images will be pulled for Snowstorm and Elasticsearch, so it will take some time until they are downloaded :
docker compose up
The containers are spinning up rather quickly and you can switch over to Docker Desktop via the "v" shortcut.
Opening up the Swagger page for Snowstorm
The Swagger page is opened up at http://localhost:8080 when the containers are running.
The next step now is to import the Snomed Ct Rf2 file. The following Powershell script can be used to import the Rf2 file. Adjust it with the Rf2 file you got. But we have downloaded the FULL Snomed-CT AU file here , so make sure you set the 'type' to FULL !
StartImportNctsSctRf2.ps1
The Powershell script below POST the Rf2 file to the imports/start-local-file-import endpoint. The upload will usually take 15-30 minutes. Since the docker-compose.yml has set up a volume, you do not have to do this everytime. The container got a volume for persistence.
$body = @{
branchPath = "MAIN"
filePath = "/rf2/NCTS_SCT_RF2_DISTRIBUTION_32506021000036107-20261031-ALL.zip"
type = "FULL"
createCodeSystemVersion = $true
} | ConvertTo-Json
Write-Host $body
Write-Host Starting import...
$response = Invoke-RestMethod `
-Uri "http://localhost:8080/imports/start-local-file-import" `
-Method Post `
-ContentType "application/json" `
-Body $body
Write-Host Retrieving response from the start-local-file-import
$response | ConvertTo-Json -Depth 10



