<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>medical imaging on Digi Hunch</title><link>https://static.digihunch.com/tag/medical-imaging/</link><description>Recent content in medical imaging on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Mon, 12 May 2025 23:27:51 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/medical-imaging/index.xml" rel="self" type="application/rss+xml"/><item><title>How imaging devices talk to each other (in DICOM)</title><link>https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/</link><pubDate>Sun, 15 Nov 2020 18:40:00 -0400</pubDate><guid>https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/</guid><description>&lt;h3 class="wp-block-heading" id="h-overview"&gt;Overview&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the &lt;a href="https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/" class="rank-math-link"&gt;previous post &lt;/a&gt;I briefly touched on DICOM as the crucial standard in medical imaging for both data exchanging and data storage. It is important to understand that DICOM is such a massive standard that, beyond data exchanging and storage, has expanded into many different areas around imaging, that no device (or information system) can ever implement every single aspect of the standard. A device or information system complies to (and implements) a subset of the DICOM standard. The manufacturer must provide a document (DICOM conformance statement) to spec out which parts of the standard are implemented. Care providers (e.g. hospitals) are supposed to review these specs as part of the procurement process to ensure interoperability with existing information system.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-medical-imaging-informatics"&gt;Medical Imaging Informatics&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When it comes to diagnosis, historical examinations provide baseline reference for radiologist. They are sometimes even more revealing than current imaging data acquired from a patient. Compared to the current exams, historical ones are referred to as priors. Priors are useful only if they are relevant to the current exams in terms of body part, modality, and exam procedure (the particular problem being studied). The effort to find out and pre-load relevant priors so they are ready to display along with current exams, is called &amp;#8220;prefetch&amp;#8221;. With huge demand in exchanging imaging data, &amp;#8220;prefetch&amp;#8221; has developed into its own sub-market in imaging informatics industry. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Trust me, this is a difficult undertaking (and why I was in the industry). First, patient identities in each healthcare organization are usually different for lack of universal medical record number; patient&amp;#8217;s name can change (marriage, divorce, or just for fun); or it can be unavailable, if patient is simply not in a condition to provide identify (e.g. trauma). Second, even if you get &amp;#8220;who&amp;#8217;s who&amp;#8221; right, you&amp;#8217;d have to dig into all his history for useful information from several different systems. The definition of relevant prior can be different depending on the specific medical specialty. Then, the old data are typically stored in a slow part of the storage from their source system, yet the patient might be bleeding and dying on the table, waiting for prior retrieval like pulling teeth. Last, but not least, the priors being retrieve might be from a modality of previous generation from 1990s; good luck with current display application. Sorry that sounds a lot but in real life, there are even more challenges. In General, there are four categories of applications that need to support DICOM:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Acquisition devices: Modalities need to store newly acquired studies persistently;&lt;/li&gt;&#10;&lt;li&gt;Routing applications: usually by the name of some routers, gateways or bridges that receive imaging studies, decorate the metadata (because a lot of legacy devices can&amp;#8217;t do it), and send to one or multiple defined destination;&lt;/li&gt;&#10;&lt;li&gt;Archives (e.g. PACS, VNA): They usually use dedicated database (metadata) and storage systems (pixel data). They are the repository of imaging data and must provide full support of transfer capability;&lt;/li&gt;&#10;&lt;li&gt;Peripheral applications that uses DICOM data, such as 3D post-processing or DICOM testing (grassroot dicom, dcmtk) &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;No matter what the devices are, they must follow certain protocols in order to communicate with each other. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-data-structure-and-encoding"&gt;Data structure and encoding&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DICOM PS3.5 defines data types in VR (value representation). Each VR has its own purpose, allowed characters, and length limit. For example:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;VR&lt;/th&gt;&lt;th&gt;Definition&lt;/th&gt;&lt;th&gt;Allowed Characters&lt;/th&gt;&lt;th&gt;Length Limit&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;SH&lt;br&gt;Short String&lt;/td&gt;&lt;td&gt;A string of characters&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;16 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LO&lt;br&gt;Long String&lt;/td&gt;&lt;td&gt;A string of characters&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;64 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AE&lt;br&gt;Application Entity&lt;/td&gt;&lt;td&gt;A string of characters that identifies a DICOM application running on a compliant device&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;16 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CS&lt;br&gt;Code String&lt;/td&gt;&lt;td&gt;A string to represent code&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;16 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PN&lt;br&gt;Person Name&lt;/td&gt;&lt;td&gt;Person&amp;#8217;s name, with caret (^) as delimiter&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;64 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UI&lt;br&gt;Unique Identifier&lt;/td&gt;&lt;td&gt;An ID that uniquely identify an item, such as 1.2.840.100008.1.1&lt;/td&gt;&lt;td&gt;0-9 and period (.)&lt;/td&gt;&lt;td&gt;64 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DA&lt;br&gt;Date&lt;/td&gt;&lt;td&gt;A string to represent date YYYYMMDD&lt;/td&gt;&lt;td&gt;0-9&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;US&lt;br&gt;Unsigned Short&lt;/td&gt;&lt;td&gt;Unsigned binary integer, 16 bits long&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SQ&lt;br&gt;Sequence of other items&lt;/td&gt;&lt;td&gt;Sequence of other items&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UN&lt;br&gt;Unknown&lt;/td&gt;&lt;td&gt;A string of bytes where the encoding of contents is unknown&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;Value Representatives&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DICOM metadata is a dataset comprised of a set of data elements. Each element includes tag, (optional) VR, length of value, and the actual value, as shown below: &lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://3.bp.blogspot.com/-hAI3mF_ZL-I/TtQOMjy6LmI/AAAAAAAAKvE/SDqvwxaXnog/s1600/DICOM+Element.png" alt=""/&gt;&lt;figcaption class="wp-element-caption"&gt;DICOM data elements&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VR is optional because it can be implicitly determined based on DICOM data dictionary defined in PS3.6. The most common tags are:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Level&lt;/th&gt;&lt;th&gt;Tag&lt;/th&gt;&lt;th&gt;Meaning&lt;/th&gt;&lt;th&gt;VR&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Patient&lt;/td&gt;&lt;td&gt;0010,0010&lt;/td&gt;&lt;td&gt;Patient&amp;#8217;s Name&lt;/td&gt;&lt;td&gt;PN&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Patient&lt;/td&gt;&lt;td&gt;0010,0020&lt;/td&gt;&lt;td&gt;Patient ID&lt;/td&gt;&lt;td&gt;LO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Patient&lt;/td&gt;&lt;td&gt;0010,0021&lt;/td&gt;&lt;td&gt;Issuer of Patient ID&lt;/td&gt;&lt;td&gt;LO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Patient&lt;/td&gt;&lt;td&gt;0010,0024&lt;/td&gt;&lt;td&gt;Issuer of Patient ID Qualifier Sequence&lt;/td&gt;&lt;td&gt;SQ&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Patient&lt;/td&gt;&lt;td&gt;0010,0030&lt;/td&gt;&lt;td&gt;Patient&amp;#8217;s Birth Date&lt;/td&gt;&lt;td&gt;DA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0008,0020&lt;/td&gt;&lt;td&gt;Study Date&lt;/td&gt;&lt;td&gt;DA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0008,0050&lt;/td&gt;&lt;td&gt;Accession Number&lt;/td&gt;&lt;td&gt;SH&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0008,0061&lt;/td&gt;&lt;td&gt;Modalities In Study&lt;/td&gt;&lt;td&gt;CS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0008,1030&lt;/td&gt;&lt;td&gt;Study Description&lt;/td&gt;&lt;td&gt;LO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0020,000D&lt;/td&gt;&lt;td&gt;Study Instance UID&lt;/td&gt;&lt;td&gt;UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0020,0010&lt;/td&gt;&lt;td&gt;Study ID&lt;/td&gt;&lt;td&gt;SH&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0008,103E&lt;/td&gt;&lt;td&gt;Series Description&lt;/td&gt;&lt;td&gt;LO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0008,0015&lt;/td&gt;&lt;td&gt;Body Part Examined&lt;/td&gt;&lt;td&gt;CS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0008,0060&lt;/td&gt;&lt;td&gt;Modality&lt;/td&gt;&lt;td&gt;CS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0020,0011&lt;/td&gt;&lt;td&gt;Series Number&lt;/td&gt;&lt;td&gt;IS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0020,000E&lt;/td&gt;&lt;td&gt;Series Instance UID&lt;/td&gt;&lt;td&gt;UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0020,0060&lt;/td&gt;&lt;td&gt;Laterality&lt;/td&gt;&lt;td&gt;CS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SOP inst&lt;/td&gt;&lt;td&gt;0008,0016&lt;/td&gt;&lt;td&gt;SOP Class UID&lt;/td&gt;&lt;td&gt;UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SOP inst&lt;/td&gt;&lt;td&gt;0008,0018&lt;/td&gt;&lt;td&gt;SOP Instance UID&lt;/td&gt;&lt;td&gt;UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SOP inst&lt;/td&gt;&lt;td&gt;0012,0010&lt;/td&gt;&lt;td&gt;Transfer Syntax UID&lt;/td&gt;&lt;td&gt;UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SOP inst&lt;/td&gt;&lt;td&gt;0020,0013&lt;/td&gt;&lt;td&gt;Instance Number&lt;/td&gt;&lt;td&gt;IS&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;Common DICOM tags&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When VR is not explicitly spelled out, the data encoding is known as implicit VR. The opposite is explicit VR, where each data element spells out the VR type. When storing numeric value, such as tags or numeric values for tags, the predominant format stores lower byte before higher bytes, known as &lt;a href="https://en.wikipedia.org/wiki/Endianness" class="rank-math-link"&gt;little endian&lt;/a&gt;. Rarely seen in DICOM is big endian, the opposite order of storing numeric values.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The tag include a group number (e.g. 0020) and element number (e.g. 0013). If group number is odd, it is a private tag not defined in PS3.6&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The actual length of tag value shall always be even number. Odd-sized value should add an additional character (e.g. trailing space), known as even-length padding to meet this requirement.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Above is the basic rules for DICOM data structure and encoding. For more information about encoding, including transfer syntax, refer to &lt;a href="https://static.digihunch.com/2018/06/dicom-data-encoding/" class="rank-math-link"&gt;this previous post&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-transactions-dimses-and-sop-classes"&gt;Transactions (DIMSEs and SOP classes)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DICOM has its own &lt;a href="http://dicom.nema.org/medical/dicom/current/output/chtml/part04/chapter_6.html" class="rank-math-link"&gt;information model &lt;/a&gt;of real world. It requires some clinical knowledge to come to full understanding. For technical people, we just need to understand the patient-study-series-image hierarchy.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;One patient may have multiple studies&lt;/li&gt;&#10;&lt;li&gt;Each study may include one or more image series&lt;/li&gt;&#10;&lt;li&gt;Each series has one or more images&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Images are also referred to as SOP instance, a general terms that include not only images, but also reports, and other types of objects.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A business transaction in DICOM is termed DIMSE, for example:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Group&lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;C-STORE&lt;/td&gt;&lt;td&gt;DIMSE-C&lt;/td&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;A stores an image to B&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C-MOVE&lt;/td&gt;&lt;td&gt;DIMSE-C&lt;/td&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;A tells B to store an image to C&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C-FIND&lt;/td&gt;&lt;td&gt;DIMSE-C&lt;/td&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;Query for patient, study, series, images&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C-ECHO&lt;/td&gt;&lt;td&gt;DIMSE-C&lt;/td&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;DICOM level &amp;#8220;ping&amp;#8221;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;N-EVENT-REPORT&lt;/td&gt;&lt;td&gt;DIMSE-N&lt;/td&gt;&lt;td&gt;Notification&lt;/td&gt;&lt;td&gt;Report an event&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;N-ACTION&lt;/td&gt;&lt;td&gt;DIMSE-N&lt;/td&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;DIMSE&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some DIMSEs involves multiple SOP classes based on the type of image being processed. For example, SOP Class UID (10.2.840.10008.5.1.4.1.1.1) represents storage for CR image. A c-store transaction includes:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Requestor (C-STORE SCU) sends a request (C-STORE-RQ), to store specified SOP class in certain transfer syntax&lt;/li&gt;&#10;&lt;li&gt;The request is followed by the actual data (PDU)&lt;/li&gt;&#10;&lt;li&gt;Once completed, the Response (C-STORE SCP) respond with C-STORE-RP&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 class="wp-block-heading" id="h-handshake-association"&gt;Handshake (Association)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the previous C-STORE example, proper syntax that are mutually supported must be used in order for the requestor and receiver to process the transaction. Both parties learn each other&amp;#8217;s supported transfer syntax through an upfront handshake process known as DICOM association.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In DICOM association, the initiating party presents a list of supported pairs of SOP class and transfer syntax. Each pair is called a presentation context, and the responding party must respond to each presentation context in the proposed list, with either yes or no.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This negotiation is similar to the cipher negotiation in TLS handshake.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For more detailed information, please refer to Pianykh&amp;#8217;s book &amp;#8220;&lt;a href="https://www.springer.com/gp/book/9783642108495" class="rank-math-link"&gt;DICOM, a practical introduction and survival guide&lt;/a&gt;&amp;#8220;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Automatic deployment of Orthanc on AWS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/11/ipvs-iptables-and-kube-proxy/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;IPVS, iptables and kube-proxy&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Automatic deployment of Orthanc on AWS</title><link>https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/</link><pubDate>Sun, 08 Nov 2020 00:54:06 -0400</pubDate><guid>https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/</guid><description>&lt;p class="wp-block-paragraph"&gt;[&lt;strong&gt;Update&lt;/strong&gt;] I changed reverse proxy from Nginx to Envoy. &lt;a href="https://static.digihunch.com/2022/03/from-nginx-to-envoy-proxy/"&gt;Here&lt;/a&gt;&amp;#8216;s the detail.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;[&lt;strong&gt;Update&lt;/strong&gt;] Some security improvement was introduced in may 2021. &lt;a class="rank-math-link" href="https://static.digihunch.com/2021/05/secure-web-application-deployment/"&gt;Here&lt;/a&gt;&amp;#8216;s detail.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;[&lt;strong&gt;Update&lt;/strong&gt;] &lt;a href="https://github.com/digihunch/orthweb"&gt;Here&amp;#8217;s&lt;/a&gt; the link to the orthweb repository.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this project we introduce a medical imaging web service based on Orthanc, an open-source project of DICOM server, and a pipeline to deploy such server automatically and consistently. We deploy Orthanc on AWS automatically. This little project involves a number of technical deets in DevOps, to deliver a web application prototype with an automated deployment pipeline.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-a-brief-on-imaging"&gt;A brief on imaging&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In medical imaging, scanning devices are the data collectors. It consists of various categories of scanners, such as Computed Tomography (CT), and Ultrasound (US). They are collectively referred to as modality, but vary significantly in terms of image generation and hardware manufacturing. The challenges to exchange data between these heterogeneous scanning devices and centralized computers came around as early as the 1980s, which brought about ACR-NEMA standard in 1985, under the initiative between American College Radiology (ACR) and National Electrical Manufacturers Association (NEMA). The standard lately evolved into DICOM (Digital Imaging Communication in Medicine), a comprehensive set of standard in the ISO framework that governs modern imaging data storage and exchange across several disciplines (radiology, cardiology, pathology, etc) that operate around images in medicine.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to defining a &lt;a href="http://dicom.nema.org/medical/dicom/current/output/chtml/part10/"&gt;file format&lt;/a&gt; to store imaging data, DICOM also includes an upper layer protocol that dictates how two compliant devices (referred as application entity, each identified by AE title) can negotiate a common syntax to transfer objects (e.g. an image, a report or a discovery). Upper layer refers to layer 5-7 in OSI model, or application layer in TCP/IP model.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-imaging-server"&gt;Imaging server&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once scanner acquires images from patient, they stores the exams to imaging server for persistent storage. The functionalities of such server expands overtime since 1990s and hence go by different names in different eras, such as PACS (Picture Archive and Communication Systems), VNA (Vendor Neutral Archive) and EI (enterprise imaging) archive. Regardless of naming, they can be generally seen as a highly specialized variation of enterprise content management system. They are usually hosted with a centralized database to index clinical information at patient, exam and image levels. The other key component is the persistent storage devices, usually in the form of a &lt;a href="https://en.wikipedia.org/wiki/Network-attached_storage" class="rank-math-link"&gt;NAS&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.orthanc-server.com/" class="rank-math-link"&gt;Orthanc&lt;/a&gt; is an open-source initiative for such imaging servers. It provides a DICOM endpoint, allowing scanning devices to store medical images. It also provides a web viewer allowing users to see the images stored. It is released for many platforms, including Docker images.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-infrastructure-as-code"&gt;Infrastructure as code&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use Amazon Web Service (AWS) for infrastructure as service, and &lt;a href="https://www.terraform.io/" class="rank-math-link"&gt;Terraform&lt;/a&gt; as the tool to provision resources off AWS, in a reliable and consistent mechanism, known as Infrastructure-as-Code. Terraform is an alternative to CloudFormation, AWS&amp;#8217;s proprietary infrastructure-as-code technology. Terraform is developed by Hashicorp as an open-source project, and therefore is vendor neutral. It supports multiple public cloud vendor through different &lt;a href="https://www.terraform.io/docs/providers/index.html" class="rank-math-link"&gt;providers&lt;/a&gt;. Each provider accesses the vendor specific SDK. For example, the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs" class="rank-math-link"&gt;AWS provider&lt;/a&gt; integrates with &lt;a href="https://aws.amazon.com/tools/" class="rank-math-link"&gt;AWS SDK&lt;/a&gt;. As a result, the code used in one vendor cannot just be applied to a different vendor without a major overhaul. Terraform&amp;#8217;s current version is 0.13 as of Oct 2020, and has gone through some &lt;a href="https://www.hashicorp.com/blog/announcing-terraform-0-12" class="rank-math-link"&gt;syntax changes&lt;/a&gt; since version 0.11. Terraform also produces files for state management locally in the working directory. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When executing, Terraform combines all files in the working directory to assess variables, and create required resources. It is compatible with the most of AWS resources. For example, you can specify user data with templates when creating EC2 instances. You can also create managed service instance as long as it is supported by the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs" class="rank-math-link"&gt;provider&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-architecture"&gt;Architecture&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Orthanc web server stores data in sqlite by default, but also has a plugin to support &lt;a href="https://wiki.postgresql.org/wiki/Main_Page" class="rank-math-link"&gt;PostgreSQL&lt;/a&gt;, an open-source relational database. AWS has managed service (&lt;a href="https://aws.amazon.com/rds/postgresql/" class="rank-math-link"&gt;RDS&lt;/a&gt;) based on PostgreSQL. In this project, we create an RDS instance that span across two availability zones for minimum high availability. Orthanc also supports storing imaging data including pixels in PostgreSQL, which obviates the need for a dedicated file storage system.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We deploy the application in Docker&amp;#8217;s containers for compatibility and portability. The Orthanc server is shipped in &lt;a class="rank-math-link" href="https://orthanc.uclouvain.be/book/users/docker.html"&gt;Docker images&lt;/a&gt;, available in &lt;a class="rank-math-link" href="https://hub.docker.com/r/jodogne/orthanc"&gt;Docker hub&lt;/a&gt; registry. The docker environment is configured as part of EC2 instance bootstrapping, including installing packages with &lt;a href="https://static.digihunch.com/2019/02/package-repository-management-in-linux/"&gt;YUM&lt;/a&gt;, initializing and customizing environment variables. The docker-compose file, and the auxiliary configuration files are provided in the repo. The bootstrapping script installs git and pulls required files from this &lt;a class="rank-math-link" href="https://github.com/digihunch/orthweb"&gt;GitHub repo&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This demo project does not include load balancing, DNS management, or container orchestration.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-security"&gt;Security&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Orthanc&amp;#8217;s web browser &lt;a href="https://orthanc.uclouvain.be/book/faq/https.html"&gt;natively supports HTTPS&lt;/a&gt;. However, the DICOM port does not support TLS natively, as their development has made clear in the &lt;a href="https://orthanc.uclouvain.be/book/faq/security.html" class="rank-math-link"&gt;FAQ&lt;/a&gt;. This leaves a severe security vulnerability because all patient data (protected health information in HIPPA context) would be sent across the Internet in the clear, visible to every network interface along the route. To address this issue we brought in Nginx as a reverse proxy to work at TCP layer to terminate encrypted traffic for Orthanc&amp;#8217;s DICOM end point. DICOM upper layer works on top of TCP layer. &lt;/p&gt;&#10;&lt;p class="has-pale-cyan-blue-background-color has-background wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="621px" viewBox="-0.5 -0.5 621 207" style="max-width:100%;max-height:207px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;path d="M 187.5 56 C 157.5 56 150 106 174 116 C 150 138 177 186 196.5 166 C 210 206 255 206 270 166 C 300 166 300 126 281.25 106 C 300 66 270 26 243.75 46 C 225 16 195 16 187.5 56 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 203px; margin-left: 151px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Internet&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="225" y="203" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Internet&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 120 98.5 L 323.64 86.38" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 328.88 86.07 L 322.1 89.98 L 323.64 86.38 L 321.69 82.99 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 95px; margin-left: 220px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Encrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="220" y="98" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Encrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="540" y="56" width="80" height="120" rx="12" ry="12" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 116px; margin-left: 541px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;DICOM &lt;br&gt;Archive&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="580" y="120" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;DICOM&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="330" y="56" width="50" height="120" rx="7.5" ry="7.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 116px; margin-left: 331px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Nginx&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="355" y="120" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Nginx&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 380 86 L 533.63 86" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 538.88 86 L 531.88 89.5 L 533.63 86 L 531.88 82.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 87px; margin-left: 456px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Unencrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="456" y="90" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Unencrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="86" width="120" height="50" rx="7.5" ry="7.5" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 111px; margin-left: 1px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;DICOM Device supporting TLS&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="60" y="115" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;DICOM Device support&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 540 146 L 386.37 146" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 381.12 146 L 388.12 142.5 L 386.37 146 L 388.12 149.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 146px; margin-left: 459px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Unencrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="459" y="149" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Unencrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 330 146 L 126.33 124.18" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 121.11 123.62 L 128.44 120.88 L 126.33 124.18 L 127.7 127.84 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 125px; margin-left: 214px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Encrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="214" y="129" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Encrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 312 6 L 312 206 M 308 206 L 308 6 M 308 206" fill="none" stroke="#6c8ebf" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 7px; margin-left: 321px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;corporate firewall&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="321" y="10" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;corporate firewall&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 142 6 L 142 206 M 138 206 L 138 6 M 138 206" fill="none" stroke="#6c8ebf" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 7px; margin-left: 151px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;corporate firewall&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="151" y="10" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;corporate firewall&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank" rel="noopener noreferrer"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Viewer does not support full SVG 1.1&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Nginx literature, this use case is referred to as &lt;a class="rank-math-link" href="https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp/"&gt;SSL Termination for TCP Upstream Servers&lt;/a&gt;. Note that Nginx is providing layer 4 capability in this use case so the certificate and key configuration should not be placed under http section of the configuration file. This layer 4 capability in fact enables security configurations of all protocol that operates in upper layers and can be used in a broad range of situations. It is also noteworthy that Nginx can re-encrypt the traffic on the way out to upstream, for even tighter security control measure as outlined in this &lt;a class="rank-math-link" href="https://docs.nginx.com/nginx/admin-guide/security-controls/securing-tcp-traffic-upstream/"&gt;use case&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;user nginx&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;worker_processes &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;error_log &lt;span style="color:#e6db74"&gt;/var/&lt;/span&gt;log&lt;span style="color:#e6db74"&gt;/nginx/&lt;/span&gt;error&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt; warn&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pid &lt;span style="color:#e6db74"&gt;/var/&lt;/span&gt;run&lt;span style="color:#e6db74"&gt;/nginx.pid;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;events {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; worker_connections 1024;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;stream {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; upstream dicom_backend {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; server orthanc-backend:4242;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; server {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; listen 11112 ssl;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; proxy_pass dicom_backend;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; ssl_certificate conf.d/&lt;/span&gt;site&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pem&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_certificate_key conf&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;d&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;site&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pem&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_protocols SSLv3 TLSv1 TLSv1&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; TLSv1&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_ciphers HIGH:&lt;span style="color:#f92672"&gt;!&lt;/span&gt;aNULL:&lt;span style="color:#f92672"&gt;!&lt;/span&gt;MD5:ECDH&lt;span style="color:#f92672"&gt;+&lt;/span&gt;AESGCM&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_session_cache shared:SSL:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;m&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_session_timeout &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;h&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_handshake_timeout &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;s&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;It is also helpful to use Nginx to terminate HTTPS traffic, using a pair of certificate and key. When testing with self-signed certificate I realized that Chrome browser has specific &lt;a href="https://support.apple.com/en-us/HT210176" class="rank-math-link"&gt;requirement&lt;/a&gt; on self-signed certificate, or it won&amp;#8217;t load the page. So the certificate has to be created as instructed &lt;a class="rank-math-link" href="https://eengstrom.github.io/musings/self-signed-tls-certs-v.-chrome-on-macos-catalina"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For better security, it is advisable that the RDS instance is provisioned in private subnet, with its data encrypted both in-transit and at-rest. Docker service should also manage sensitive information as &lt;a href="https://docs.docker.com/engine/swarm/secrets/" class="rank-math-link"&gt;secrets&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-summary"&gt;Summary&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The deliverable is stored in this Github &lt;a href="https://github.com/digihunch/orthweb" class="rank-math-link"&gt;repo&lt;/a&gt;. The docker part of it can be executed on MacBook with PostgreSQL. The entire hardware stack represented by terraform code, can be executed against AWS to create required resources. Checkout README for further instruction. To emulate a modality, one will need a TLS supported DICOM application entity, &lt;a href="https://horosproject.org/" class="rank-math-link"&gt;Horos&lt;/a&gt; is a great project on MacOS to serve this purpose, both as DICOM-compliant sender and a viewer. Alternatively, consider some command-line based DICOM toolkit such as &lt;a href="https://support.dcmtk.org/redmine/projects/dcmtk" class="rank-math-link"&gt;dcmtk&lt;/a&gt;, or &lt;a href="https://sourceforge.net/projects/gdcm/" class="rank-math-link"&gt;grassroot dicom&lt;/a&gt;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/11/docker-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Docker storage&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;How imaging devices talk to each other (in DICOM)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>