Run several installations on one server
This page is for parties that host Linksight for several organizations. It shows how to run a separate Linksight installation per organization on one Docker server, so you don't need a virtual machine per organization.
An installation here means everything one docker-compose.yml starts: the Linksight Supervisor for Docker (the supervisor), plus the data station and Analysis Hub that it deploys. Each organization gets its own installation, registered under its own organization on the Governance Hub.
Installations on one server can't share the same host ports. You can solve that in one of two ways:
| Setup | How partners reach each data station | Platforms | Supervisor version |
|---|---|---|---|
| Separate ports | Each data station uses its own ports, for example 8443/9090 and 8444/9091. | Linux and Windows Server | Any |
| Shared ports behind an SNI proxy | All data stations use 8443/9090. A proxy forwards each connection by the data station's DNS name. | Linux with Docker Engine 25 or later | v1.1.10 or later |
Separate ports is the simpler setup, but every installation adds two open data station ports to the server and to your firewall. Use the SNI proxy to keep the number of open data station ports to two, 8443 and 9090, however many installations the server runs. Partners then only need to allow those two ports as well.
This page does not cover Kubernetes deployments, or running more than one data station under a single supervisor.
The supervisor controls Docker through the Docker engine socket. Anyone with administrator access to the server or to Docker can read the data of every installation on it. Make sure each hosted organization agrees to this before you install it next to others.
Before you start
Check the following before you add a second installation:
- Server resources. Every installation needs the minimum specifications of a single installation, see Deploy using Docker or Deploy on Windows Server. A data station uses all CPU cores during a computation, so installations that compute at the same time slow each other down.
- Registration rights. Each installation is registered under its own hosted organization, by an
IT adminorOrganization adminof that organization. If you don't have one of these roles in a hosted organization, that organization's administrator registers the installation, or invites you with one of those roles. - One working directory and one data folder per installation. Start every new installation in a new, empty working directory with its own
FULLDATAPATH. Give every working directory a unique name, for example~/linksight/org-aand~/linksight/org-b. Docker Compose names the project after this directory, so two installations in directories with the same name replace each other's supervisor. Names that only differ in upper and lower case count as the same name. - Permissions on Linux. The commands on this page run
dockerwithoutsudo. Addsudoif your user is not in thedockergroup, see Deploy using Docker.
The supervisor stores its ID in the data folder and names its containers, network and volumes after that ID. If two installations use the same data folder, or one starts from a copy of another's folder, both supervisors take over the same containers.
If an installation already runs on the server
Treat the installation that already runs as the first installation, and keep its working directory and data folder. Some of its settings change as well: the memory limit, and for Option 2 the settings in step 6. To apply a change:
- Edit the
.envfile in its working directory. - In the same directory, run
docker compose up -d(on Windows Server:docker-compose up -d).
The supervisor then restarts and recreates the data station and the Analysis Hub with the new settings, which interrupts that installation for a moment. If you change DATASTATION_HOST, for example from an IP address to a DNS name, the data station announces its new address to its partners when it starts.
Use Let's Encrypt for one installation at most
Automatic certificate issuance needs public port 80 (for http-01) or 443 (for tls-alpn-01), and only one installation can use each port. Enable ANALYSISHUB_AUTOCERT_ENABLED in one installation at most. If a reverse proxy already uses ports 80 and 443, no installation can use it. Give the other installations their own certificates, or let the reverse proxy handle TLS.
Choose the settings for each installation
The following settings must differ between installations. Keep the defaults for the first installation, and pick unused values for every next one:
| Setting | First installation | Next installation |
|---|---|---|
| Working directory | ~/linksight/org-a | ~/linksight/org-b |
FULLDATAPATH | /home/john/linksight/org-a/data | /home/john/linksight/org-b/data |
ANALYSISHUB_FRONTEND_FQDN | http://ah.example.org:8000 | http://ah.example.org:8001 |
ANALYSISHUB_PORT | 8000 | 8001 |
ANALYSISHUB_GRPC_PORT | 8080 | 8081 |
DATASTATION_PORT | 8443 | 8444 |
DATASTATION_GRPC_PORT | 9090 | 9091 |
NETWORK_SUBNET | empty | empty |
Keep the following in mind when you pick the values:
- On Windows Server, use paths like
C:\linksight\org-aandC:\linksight\org-a\data. FULLDATAPATHis an absolute path. For a new installation, the folder is empty when the installation starts for the first time.ANALYSISHUB_FRONTEND_FQDNmust matchANALYSISHUB_PORT, or be the URL of the reverse proxy.DATASTATION_PORTandDATASTATION_GRPC_PORTonly differ in Option 1. In Option 2, every installation keeps8443and9090.- Leave
NETWORK_SUBNETempty, or give each installation a different subnet. SUPERVISOR_GRPC_PORTdoes not need to differ: it is only used inside each installation's own Docker network.
Check which ports are already in use
To see which ports the installations on the server already use, run:
docker ps --format "table {{.Names}}\t{{.Ports}}"
To see the ports that other programs use, run:
- Linux
- Windows Server
sudo ss -ltn
Get-NetTCPConnection -State Listen | Sort-Object LocalPort | Select-Object LocalAddress, LocalPort
Get-NetNatStaticMapping | Select-Object ExternalPort, InternalIPAddress, InternalPort
If an installation uses a port that is already taken, its containers don't start, and the supervisor logs that the port is already allocated.
Split the memory
By default, each data station may use 75% of the server's memory. With several installations, decide how they share the server's memory, and set one of the following in every installation's .env:
SUPERVISOR_DATASTATION_MEMORY_HOSTFRACTION: a fraction of the server's memory, for example0.35.SUPERVISOR_DATASTATION_MEMORY_LIMIT: an absolute size, for example6GiB. This setting takes precedence over the fraction.
These settings need supervisor v1.1.8 or later.
You can split the memory in one of two ways:
- A fixed share per installation (recommended). Set the limits so that together they fit in the server's memory, and leave room for the Analysis Hubs and the operating system. For example
0.35each for two installations. Installations can compute at the same time without affecting each other, but an installation can't use the memory that idle installations leave unused. - Overlapping limits. Set limits that together exceed the server's memory, for example
0.6each for two installations. An installation can then use more memory while the others are idle. If several installations compute at the same time and the server runs out of memory, the operating system stops a data station: its computation fails, and the data station restarts. Choose this only if the installations rarely compute at the same time.
For an installation that already runs, apply the change as described in If an installation already runs on the server.
Option 1: Separate ports
In this setup, every installation publishes its data station and Analysis Hub on its own host ports. The data station registers its host and ports with the Linksight platform, so partners automatically connect to the right ports. All installations can share the same DATASTATION_HOST.
The following diagram shows two installations on their own ports:
Install each installation
For every new installation, do the following. The commands use org-b as an example.
-
Create its working directory and go to it:
- Linux
- Windows Server
mkdir -p ~/linksight/org-b
cd ~/linksight/org-bNew-Item -ItemType Directory -Path C:\linksight\org-b
Set-Location -Path C:\linksight\org-b -
Download the files and configure
.envas described in Deploy using Docker or Deploy on Windows Server. Use the values you chose in Choose the settings for each installation. -
Start the installation, as described on the same page.
-
Register the installation under its hosted organization:
- Open the installation's Analysis Hub at its
ANALYSISHUB_FRONTEND_FQDN, and clickConnect. - On the Governance Hub, check that the hosted organization is selected, not your own organization, and click
Accept.
If you don't have the
IT adminorOrganization adminrole in the hosted organization, send the Analysis Hub URL to that organization's administrator, who does these two steps. See Register nodes for the full procedure. - Open the installation's Analysis Hub at its
Open the firewall for each installation
Each installation after the first needs its own ports opened. The following rules are needed per installation, next to the rules for a single installation. Server stands for the IP address of the server that runs the installations.
| Direction | Protocol | Port | Source | Destination | Purpose |
|---|---|---|---|---|---|
| INGRESS | TCP | DATASTATION_PORT (e.g. 8444) | Any (0.0.0.0/0) | Server | mTLS HTTP server of this data station |
| INGRESS | TCP | DATASTATION_GRPC_PORT (e.g. 9091) | Any (0.0.0.0/0) | Server | mTLS gRPC server of this data station |
| INGRESS | TCP | ANALYSISHUB_PORT (e.g. 8001) | The organization's users | Server | Analysis Hub frontend |
| INGRESS | TCP | ANALYSISHUB_GRPC_PORT (e.g. 8081) | The organization's users | Server | Analysis Hub API, for the Linksight Python Interface |
As for a single installation, we recommend limiting the source of the data station ports to the data stations of partner organizations.
On Windows Server, also allow the ports in the server firewall, for example:
netsh advfirewall firewall add rule name="Allow Linksight mTLS org-b" dir=in action=allow protocol=TCP localport=8444
netsh advfirewall firewall add rule name="Allow Linksight gRPC org-b" dir=in action=allow protocol=TCP localport=9091
netsh advfirewall firewall add rule name="Allow Linksight Analysis Hub org-b" dir=in action=allow protocol=TCP localport=8001,8081
Keep the following in mind for the partner connections:
- Outgoing traffic. Each data station connects to its partners on the ports that the partners registered. If your firewall only allows outgoing traffic to ports
8443and9090, partners that are hosted on other ports are unreachable. In the same way, partners whose firewall only allows outgoing8443and9090must also allow the ports of your other installations. - Organizations on the same server. If two hosted organizations take part in the same data collaboration, their data stations connect to each other through the server's public address. Your perimeter firewall must then support NAT loopback (also called hairpinning). Otherwise, use DNS names as
DATASTATION_HOST. In each installation, setCONTAINERS_EXTRA_HOSTSto map the names of all data stations on the server to the server's internal address, for exampleds-org-a.example.org:10.0.0.5,ds-org-b.example.org:10.0.0.5. - One IP address for all data stations. Partners see the same IP address for every data station on the server. A partner can't allow one hosted data station by IP address without also allowing the others.
Check that the installations work
Check the following after you start and register an installation:
docker psshows a set of containers per installation (supervisor, data station and Analysis Hub). The containers of each installation share a name suffix, and each set uses its own host ports.- Each Analysis Hub loads at its own
ANALYSISHUB_FRONTEND_FQDN. - Once an installation is part of a data collaboration, its data station shows as online on the Network Status page of the Governance Hub.
Option 2: Shared ports behind an SNI proxy
In this setup, every data station is reachable on the server's ports 8443 and 9090, under its own DNS name. A proxy in front of the data stations reads the DNS name that a partner connects to from the start of the TLS connection (the Server Name Indication, or SNI), and forwards the connection to that data station. The proxy does not decrypt anything: the mutual TLS connection still runs end to end between the two data stations.
This setup is available on Linux only, and needs Docker Engine 25 or later.
The setup works as follows:
- Each data station has its own DNS name (its
DATASTATION_HOST), and all names point to the server's public IP address. Partners send that name as SNI when they connect. - The data stations don't publish ports on the server (
SUPERVISOR_DATASTATION_PUBLISH_PORTS=false). Only the proxy listens on8443and9090. - The proxy and the data stations share an internal Docker network. This page names it
linksight-edge, but you can choose another name. Each data station joins it under itsDATASTATION_HOST(SUPERVISOR_DATASTATION_EXTRA_NETWORK=linksight-edge), so the proxy can forward to that name. - The Analysis Hubs are not behind this proxy. Each one keeps its own published ports, as in Option 1.
The following diagram shows the proxy in front of the data stations:
Proxy requirements
You can use any proxy that meets the following requirements:
- It runs as a container on the server, attached to the
linksight-edgeDocker network, and publishes ports8443and9090through a second, normal Docker network.linksight-edgeis internal, and Docker doesn't publish ports from an internal network. - It routes each connection by the SNI to the data station with that
DATASTATION_HOST, on the same port. - It passes the TLS connection through unchanged. No SSL offloading, no TLS termination and no PROXY protocol towards the data stations: they break the mutual TLS between data stations. See SSL Offloading, Deep Packet Inspection and Web Application Firewalls.
- It looks up a data station's name again when the address changes, because a data station gets a new address on the Docker network every time it is recreated.
- It starts even when a data station isn't running yet.
- Its idle timeouts for client and server connections are 60 seconds or more. Data stations ping each other every 30 seconds on active connections.
The steps below use HAProxy as an example.
Example: Set up the proxy and the installations with HAProxy
The example pulls the HAProxy image from Docker Hub, which is not part of the required connections: allow registry-1.docker.io, auth.docker.io and production.cloudflare.docker.com during installation, or use a mirror.
-
Create a DNS
Arecord for every data station, all pointing to the server's public IP address. For exampleds-org-a.example.organdds-org-b.example.org.cautionUse DNS names, not IP addresses. Partners don't send an SNI when they connect to an IP address, so the proxy can't tell the data stations apart.
-
Create the shared Docker network as an internal network:
docker network create --internal linksight-edgeThe data stations also join this network. Docker picks a container's default route by network name, so a normal network named
linksight-edgewould take over the data stations' outgoing traffic from their own network. An internal network has no route out, so it can't. The supervisor logs a warning if the network is not internal. -
Create a working directory for the proxy:
mkdir -p ~/linksight/sni-proxy
cd ~/linksight/sni-proxy -
In that directory, create the following two files. In
haproxy.cfg, replaceds-org-a.example.organdds-org-b.example.orgwith the DNS names of your data stations. For more than two data stations, see Add or remove a data station.docker-compose.yml
services:
haproxy:
image: haproxy:3.2
restart: always
ports:
- "8443:8443"
- "9090:9090"
volumes:
# Mount the directory, not the file: editors that save by replacing
# haproxy.cfg would otherwise leave the container on the old version.
- ./:/usr/local/etc/haproxy:ro
networks:
# default: for the published ports; linksight-edge: to the data stations
- default
- linksight-edge
networks:
linksight-edge:
external: truehaproxy.cfg
global
log stdout format raw local0
defaults
mode tcp
log global
log-format "%ci:%cp %ft %b/%s sni=%[capture.req.hdr(0)] %Tt %B %ts"
timeout connect 5s
timeout client 5m
timeout server 5m
# Docker's DNS, so a backend follows its data station when it is recreated.
resolvers docker
nameserver dns 127.0.0.11:53
hold valid 10s
frontend datastation_https
bind :8443
tcp-request inspect-delay 5s
tcp-request content capture req.ssl_sni len 253
tcp-request content accept if { req.ssl_hello_type 1 }
use_backend ds1_https if { req.ssl_sni -i ds-org-a.example.org }
use_backend ds2_https if { req.ssl_sni -i ds-org-b.example.org }
frontend datastation_grpc
bind :9090
tcp-request inspect-delay 5s
tcp-request content capture req.ssl_sni len 253
tcp-request content accept if { req.ssl_hello_type 1 }
use_backend ds1_grpc if { req.ssl_sni -i ds-org-a.example.org }
use_backend ds2_grpc if { req.ssl_sni -i ds-org-b.example.org }
# Connections whose SNI matches no data station are closed.
backend ds1_https
server ds1 ds-org-a.example.org:8443 resolvers docker init-addr none
backend ds1_grpc
server ds1 ds-org-a.example.org:9090 resolvers docker init-addr none
backend ds2_https
server ds2 ds-org-b.example.org:8443 resolvers docker init-addr none
backend ds2_grpc
server ds2 ds-org-b.example.org:9090 resolvers docker init-addr none -
Start the proxy:
docker compose up -dIf an installation on the server still publishes
8443and9090, the proxy can't start. Move that installation behind the proxy first, with the settings from step 6, as described in If an installation already runs on the server. -
Install and register each installation as in Option 1, with the following values in its
.env:Setting Value Notes DATASTATION_HOSTds-org-a.example.orgThis installation's own DNS name. DATASTATION_PORT8443The same for every installation. DATASTATION_GRPC_PORT9090The same for every installation. SUPERVISOR_DATASTATION_PUBLISH_PORTSfalseSUPERVISOR_DATASTATION_EXTRA_NETWORKlinksight-edgeThe other settings in Choose the settings for each installation still differ per installation: the working directory,
FULLDATAPATH, the Analysis Hub settings and the memory limits.Keep the
SUPERVISOR_VERSIONvariable at its default value, so the supervisor is recent enough. -
In the firewall, open ports
8443and9090to the server for partners, as for a single installation. No extra data station ports are needed per installation. Each installation still needs its Analysis Hub ports, as in the Option 1 firewall rules.
Check that the proxy forwards the connections
To test the routing before partners connect, run the following from a machine outside the server, for every data station and for both ports 8443 and 9090:
openssl s_client -connect <server public IP>:8443 -servername ds-org-a.example.org </dev/null
The TLS handshake fails, because the test has no client certificate. That is expected: what matters is the proxy log. On the server, run:
cd ~/linksight/sni-proxy
docker compose logs -f haproxy
A line like datastation_https ds1_https/ds1 sni=ds-org-a.example.org means the connection reached that data station. A line with <NOSRV> means the SNI didn't match any data station in haproxy.cfg. <NOSRV> lines without an SNI come from scanners on the internet, and are harmless.
Once the installations are registered and part of a data collaboration, their data stations show as online on the Network Status page of the Governance Hub.
Add or remove a data station
Each data station has four entries in haproxy.cfg: a use_backend line in the datastation_https and datastation_grpc frontends, and two backends.
To add a data station, for example ds-org-c.example.org:
-
Create its DNS record, as in step 1.
-
Add its four entries to
haproxy.cfg:# in frontend datastation_https
use_backend ds3_https if { req.ssl_sni -i ds-org-c.example.org }
# in frontend datastation_grpc
use_backend ds3_grpc if { req.ssl_sni -i ds-org-c.example.org }
backend ds3_https
server ds3 ds-org-c.example.org:8443 resolvers docker init-addr none
backend ds3_grpc
server ds3 ds-org-c.example.org:9090 resolvers docker init-addr none -
Check the configuration and reload the proxy, as described below.
-
Install the installation with the values from step 6.
To remove a data station, delete its four entries from haproxy.cfg, then check the configuration and reload the proxy.
To check the configuration and reload the proxy, run:
cd ~/linksight/sni-proxy
docker compose exec haproxy haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg
docker compose kill -s HUP haproxy
The reload keeps open connections. If the check reports an error, fix it before you reload: a reload with an invalid configuration keeps the old configuration running.
Things to know about this setup
- One IP address for all data stations. Partners see the same IP address for every data station on the server. If a partner allows data stations by IP address, one rule covers all of them.
- Organizations on the same server. Data stations on the same server reach each other directly over
linksight-edge, so no NAT loopback is needed. - Start order. Create the
linksight-edgenetwork before you start an installation. If the network is missing, the supervisor doesn't start the data station, logs an error, and tries again every 20 seconds.
Serve the Analysis Hubs behind one reverse proxy
Instead of giving every Analysis Hub its own port, you can serve all of them on port 443 behind one reverse proxy, with one FQDN per organization. The reverse proxy terminates TLS for the Analysis Hubs, and forwards each FQDN to that installation's ANALYSISHUB_PORT on the server.
For each installation, configure the following:
- Set
ANALYSISHUB_FRONTEND_FQDNto the URL the organization's users open, for examplehttps://analysis.org-a.example.org. - Keep a unique
ANALYSISHUB_PORTper installation, and forward the FQDN to it on the server's address, for examplehttps://analysis.org-a.example.org→http://<server address>:8000. - Leave
ANALYSISHUB_FRONTEND_TLS_CERTFILE,ANALYSISHUB_FRONTEND_TLS_KEYFILEandANALYSISHUB_AUTOCERT_ENABLEDempty: the reverse proxy handles TLS. - The supervisor still publishes
ANALYSISHUB_PORTon all of the server's addresses. Allow that port in the firewall only for the reverse proxy, so users can't bypass it over plain HTTP. - The Linksight Python Interface connects to
ANALYSISHUB_GRPC_PORTdirectly. Keep that port reachable for the organization's users.
The data station ports must bypass this reverse proxy. Partners connect to the data stations directly (Option 1) or through the SNI proxy (Option 2), never through a proxy that terminates TLS.
Updates and restarts
Each installation updates and restarts on its own. An update or restart started from an Analysis Hub only affects that installation's containers. The other installations keep running.
Troubleshooting
| Symptom | Cause | Solution |
|---|---|---|
| Containers don't start and the supervisor logs that the port is already allocated | Another installation or program uses the same port. In Option 2: an installation still publishes 8443/9090, or its supervisor is older than v1.1.10. | Pick an unused port, see Check which ports are already in use. In Option 2, check the installation's .env, and check its supervisor version with docker compose logs supervisor | grep "lsd - ". |
| Starting an installation replaces another installation's supervisor | Both working directories have the same name, so Docker Compose treats them as one project. | Run docker compose down in one of the directories, rename that directory to a unique name, and start both installations again with docker compose up -d. |
| Two installations stop or restart each other's containers | They use the same data folder, or one started from a copy of the other's data folder. | Give each installation its own, initially empty, FULLDATAPATH. |
| The second supervisor can't create its network | Both installations set the same NETWORK_SUBNET. | Leave NETWORK_SUBNET empty, or give each installation a different subnet. |
| Data stations are stopped during computations | The memory limits of all installations together exceed the server's memory, and several installations computed at the same time. | Lower the limits so that they fit in the server's memory, see Split the memory. |
(Option 2) The data station isn't started, and the supervisor logs SUPERVISOR_DATASTATION_EXTRA_NETWORK 'linksight-edge' with not found | The linksight-edge network doesn't exist. | Run docker network create --internal linksight-edge. The data station starts within 20 seconds. |
(Option 2) The data station isn't started, and the supervisor logs Container cannot be connected to network endpoints | Docker Engine is older than version 25. | Upgrade Docker Engine to version 25 or later, or use Option 1. |
(Option 2) The data station isn't started, and the supervisor logs invalid SUPERVISOR_DATASTATION_PUBLISH_PORTS | The value is not true or false. | Set exactly true or false, and apply the change. |
(Option 2) Partners can't connect, and the proxy logs <NOSRV> with their data station's name | The name is missing or misspelled in haproxy.cfg. | Fix the name, then check the configuration and reload the proxy. |
(Option 2) A change to haproxy.cfg has no effect | The configuration has an error, so the reload kept the old configuration. | Run the configuration check, fix the reported error, and reload again. |
To see the supervisor's log, run docker compose logs supervisor in the installation's working directory (on Windows Server: docker-compose logs supervisor).