Server

Open mSupply server is a component of the Open mSupply system. The server:

  • Hosts the server web interface and exposes RESTful and GraphQL APIs for open mSupply data.
  • Synchronises with central servers which implement v5 of the mSupply sync API.
  • Exposes a dynamic plugin system for customising and extending functionality.
  • Can act as a remote or central omSupply server, see Synchronisation documentation

Quick Start

Remote server can use sqlite or postgres, quick start guide is for sqlite flavour.

Dependencies

N.B. The version of rust used by open mSupply as of July 2025 is 1.88, so, once you've installed rust in the guides below, set up your environment to use that version. You can do that by running rustup install 1.88 and then rustup default 1.88.

When changing to newer rust version please make sure to update docker build container as per this commit.

Windows

  • Follow Microsoft's Rust installation guide
  • Install perl
  • For building the windows binary, you'll need to install the Bonjour Windows SDK and configure the environment variable BONJOUR_SDK_HOME to point to the installation location. This is required for the dns-sd implementation on windows, used for server discovery.

Mac

brew install libpq and add the following to ~/.cargo/config.toml

[env]
MACOSX_DEPLOYMENT_TARGET = "10.12"

[target.aarch64-apple-darwin]
rustflags = "-L /opt/homebrew/opt/libpq/lib"

Ubuntu

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • After a fresh rustup installation start a new bash to make env variable available
  • Install packages (e.g. needed to install/compile openssl-sys):
sudo apt install make gcc pkg-config libavahi-compat-libdnssd-dev libpq-dev

Set up sync with mSupply central

Remote server data is configured through mSupply central server, when the app first starts it's expected to initialise from mSupply.

Set up Open mSupply remote site in mSupply

  • Ensure you have mSupply set up locally
  • In Admin > Preferences > General, ensure Synchronisation is active is selected
  • In Special > Synchronisation
    • Open (or create) the site for our Open mSupply instance
    • Does it have a hardware ID already? If so, refresh the hardware ID so it doesn't have one.
  • Initialise your Open mSupply instance - you can do this two ways:
    • Via the UI (this may be the easier option when you are switching between different APIs):
      • After starting the server & client, you should see an initialisation screen
      • Enter the site details from mSupply
    • Via YAML configuration:
      • In your Open mSupply repo, under server/configuration copy the example.yaml to local.yaml, and uncomment the contents of the file. Under the sync config, ensure the username is set to the site name from mSupply (i.e. remote)
      • Your Open mSupply instance should sync with mSupply automatically when you start the server
  • After the initial sync, you generally shouldn't need mSupply running to run Open mSupply

OR: Run without mSupply central

Instead of initialising from mSupply, there is also a cli option to initialise from previously exported initialisation:

cargo run --bin remote_server_cli -- initialise-from-export -n [export name]

Where [export name] is name of exported data in data/ folder

# example
cargo run --bin remote_server_cli -- initialise-from-export -n reference1

Above will create a SQLite database file in your current working directory (typically server/), using the name from configuration/*.yaml (by default omsupply-database.sqlite), and populate it with data. Towards the end of the CLI output a user:password list is printed (those users can be used to log in via the client/API).

NOTE: initialise-from-export currently emits a large number of warnings (see https://github.com/msupply-foundation/open-msupply/issues/10241). The command should still complete and print a list of username/passwords. Those credentials are also listed in data/reference1/users.txt.

Start the server

Now we can start server with

cargo run

NOTE: make sure that sync configurations in configuration/*.yaml files are commented out if running without mSupply Central, otherwise you may get an error stating that the database and yaml sync configurations differ (in which case, the remote server will try to contact central server)

Explore API available on http://localhost:8000/graphql with build in playground or try online graphiql explorer

Open mSupply Central and Remote

Open mSupply server can be launched in both modes, this is controlled via Site is open mSupply central server configuration in legacy mSupply site settings, more info in Sync Docs and Integration Test Docs.

From version 2.0 omSupply would require both legacy and omSupply central server for full configuration/synchronisation. To start both omSupply central and omSupply remote:

  • Create two sites as per above "Set up sync with mSupply central" instructions
  • Configure one site to be central server by checking Site is open mSupply central server and entering the URL with which remote sites can reach this site in the field This site url
  • cargo run twice but change port, database and sync settings in yaml file or overwrite with env variables

For example, two sites running locally from the same repo, central and test, for central site Site is open mSupply central server is checked and This site url is http://localhost:2055. Comment out all sync settings in yaml and can start central with APP__SERVER__PORT=2055 APP__DATABASE__DATABASE_NAME="central_test" cargo run front end would be started with yarn start -- -- --env API_HOST='http://localhost:2055' --port 3005 (--port is for webpack port), and then start test with cargo run and yarn && yarn start-local from respective folders. The first site would be initialised with central site credentials first, and second sites with test credentials, test site would sync with both legacy mSupply and omSupply central server (this central site), and central site would synchronise with legacy mSupply server only

Start server in watch mode

Sometimes it's nice to have your dev server automatically rebuild and restart on changes.

You'll need to install cargo-make and cargo-watch globally:

cargo install cargo-make cargo-watch

Then, to start the server in watch mode:

cargo make watch

Note: watch mode may not always be a part of your desired workflow - particularly when you are making lots of backend changes, it's another competitor for the build directory file lock on every save (alongside tests, rust-analyzer...) and Rust compilation isn't known for its speediness 😁

It's great for cases such as doing frontend work, where you want the backend to refresh as you switch/pull branches, or when making minor backend changes. Or you can play around with debounced re-compile times, with the cargo-watch --delay flag.

Start with feature flags

Feature flags can be used to enable/disable certain features. Mostly we use them to hide features in the that are not ready for production. See Feature Flags hook for currently available flags.

To enable a feature, easiest way is to set the variable in your configuration yaml file, e.g.

features:
  example_feature: true

See Config section for more info on yaml configurations.

Run with postgres

Dependencies

When using Postgres, Postgres 12 or higher is required.

Windows

  • Install PostgreSQL.
  • Locate your PostgresSQL installation directory (e.g. C:\Program Files\PostgreSQL\14\).
  • Update Path and PQ_LIB_DIR environment variables:
> $env:PQ_LIB_DIR='C:\Program Files\PostgreSQL\14\lib'
> $env:Path+='C:\Program Files\PostgreSQL\14\lib;C:\Program Files\PostgreSQL\14\bin'
  • To persist Path and PQ_LIB_DIR for all future sessions, paste the following into a powershell terminal (requires administrator privileges):
# CAUTION: this is irreversible!
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $env:Path
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PQ_LIB_DIR -Value $env:PQ_LIB_DIR

Mac

Ubuntu

  • Install Postgres:
sudo apt install postgresql

Optional

Postgres rust feature

Remote server by default will run with sqlite database (sqlite rust feature), postgres can be turned on with postgres feature.

i.e. to run without mSupply central, as per above

  • Make sure that postgres credentials are correct in configuration/*.yaml (database will be automatically created)
cargo run --bin remote_server_cli --features postgres -- initialise-from-export -n reference1
cargo run --features postgres

Database CLI

You can manually create and migrate database with the following

  • Settings in configurations/*.yaml will be used for credentials and database name
# postgres
cargo run --bin remote_server_cli --features postgres -- initialise-database
# sqlite
cargo run --bin remote_server_cli -- initialise-database

Sharing SQLite Database files

When using sqlite, open-mSupply enables a feature called Write Ahead Log (WAL), this uses a separate file to improve concurrent access to the data. If you want to ensure all your changes have been written to the main sqlite database file, you may need to run the VACUUM command against your database.

sqlite3 omsupply-database 'VACUUM;'

Configs

note: yaml configurations are likely to be deprecated to .env, thus documentations is limited for .yaml.

In configurations folder you'll find .yaml config files, there is base, local (will overwrite/expand base in dev mode), production (will overwrite/expand other configs when APP\_\_ENVIRONMENT=production ).

Config files can be selected with the --config-path argument. When this argument is not provided configuration will default to local.yaml/production.yaml as per above. When providing a file path, the code expects a base.yaml file to exist at the same directory level as the chosen file.

cargo run -- --config-path configurations/alternate_file.yaml

You can use env variable to overwrite any configurations, can use dot notation with __ (two underscore) to specify nested value. Env vars configuration overrides start with APP__.

# example, will overwrite sync.url config in yaml
APP__SYNC__URL='http://localhost:8001' cargo run

Export initialisation

If you have mSupply central running and want to export initialisation

  • Note sync credentials from configurations/*.yaml or env vars will be used
cargo run --bin remote_server_cli -- export-initialisation -n [name of export] -u [users]
# example
cargo run --bin remote_server_cli -- export-initialisation -n 'reference2' -u 'user1:password1,user2:password2'
# password in configurations/.*yaml is hashed, can use -p option to specify unhashed password
cargo run --bin remote_server_cli -- export-initialisation -n 'reference2' -u 'user1:password1,user2:password2' -p 'syncpassword'

SSL/https

  • To enable ssl place the key.pem and cert.pem files into the app_data/certs directory.
  • Update the server.host variable in the configuration if needed
  • In production (-release build) server must be running with ssl

Use a self signed cert, e.g. for testing

# Ensure certs directory exits
mkdir -p app_data/certs
# Testing cert for CN=localhost
openssl req -x509 -newkey rsa:4096 -nodes -keyout app_data/certs/key.pem -out app_data/certs/cert.pem -days 365 -subj '/CN=localhost'

Test

Devs should run both postgres and sqlite tests before publishing a PR. The CI uses cargo nextest which is an alternative test runner tailored for use with CI. By default it runs crates in parallel which should speed up test execution times.

More info: https://nexte.st/

  • To run all tests:
# Install nextest runner
cargo install cargo-nextest --locked
# Use sqlite (sqlite is default feature)
cargo nextest run
# Use postgres
cargo nextest run --features postgres
  • To run email tests: Start mailhog (or similar email capture service). For install instructions visit: https://github.com/mailhog/MailHog
cargo test --features=email-tests --package service --lib -- email_test --nocapture
  • To run sync integration test

See Sync Integration Tests

Note, to speed-up tests database templates are used (Sqlite + Postrgres), e.g. to reuse fully migrated databases from previous tests. To disable this you can set the environment variable MSUPPLY_NO_TEST_DB_TEMPLATE=true. However, in general this should not be needed but might be useful if you suspect a bug in the test template code.

Building docs

Docs are built via github action, but can build local version with docker: how to build docs locally

CORS settings

By default remote-server limits Cross-Origin Resource Sharing to the origins configured in the server section of the configuration yaml. This is a security mechanism to reduce the risk of a malicious site accessing an authenticated connection with mSupply. Rust enforces the allowed origins in requests, even if the browser doesn't, by returning a 400 error when the Origin isn't specified in a request, or if doesn't match one of origins configured in cors_origins.

Set the cors_origins section of the yaml to include any URLs you want to access Open mSupply's GraphQL API from this includes the url for the omsupply-client you are using. e.g. local.yaml

server:
  port: 8000
  cors_origins: [http://localhost:3003, https://youwebserver:yourport]

In development mode (if not built with --release) cors is set to permissive (server will return allow origin = requesting origin)

server:
  port: 8000
  cors_origins: [http://localhost:3003, https://youwebserver:yourport]

Serving front end

Server will serve front end files from (client/packages/host/dist), if the client was not build and the folder is empty, server will return an error message: Cannot find index.html. See https://github.com/openmsupply/open-msupply#serving-front-end.

You can build front end by running yarn build from client directory in the root of the project. After that if you run the server you can navigate to http://localhost:port to see this feature in action.

When app is built in production mode (with build --release) static files will be embedded in the binary. To build, run yarn build command from the root of repository. This will build the client application and then build the release version of the server, bundling in the client so that it can be hosted.

Cli

Some common operations are available via cli remote_server_cli, the --help flag should give a detailed explanation of how to use cli. Here are example of all of the currently available commands. note: configurations from configurations/*.yaml or env vars will be used when running cli commands.

note: All of the cli commands are meant for development purposes, some commands are dangerous to run in production

# overall help
cargo run --bin remote_server_cli -- --help
# help for an action
cargo run --bin remote_server_cli -- initialise-from-export --help
# exports graphql schema to schema.graphql file
cargo run --bin remote_server_cli -- export-graphql-schema
# initialise database (creates or replaces existing database and initialises schema)
cargo run --bin remote_server_cli -- initialise-database
# by default all commands will run with sqlite database, use --features postgres to use postgres database (not applicable to export-initialisation or export-graphql-schema action)
cargo run --bin remote_server_cli --features postgres -- initialise-database
# export initialisation data from mSupply central to `data/export_name` folder (IMPORTANT: Should not be used on large data files, as single JSON format is not suited for large set of data)
cargo run --bin remote_server_cli -- export-initialisation -n 'export_name' -u 'user1:password1,user2:password2' -p 'sync_password'
# initialise database from initialisation export (will replace current data), and in this case -r flag will attempt to advance all historic date and date/times forward.
cargo run --bin remote_server_cli -- initialise-from-export -n 'export_name' -r
# initialise from mSupply central (will replace current data)
cargo run --bin remote_server_cli -- initialise-from-central -u 'user1:password1'
# attempt to refresh dates (advance them forward, see --help)
cargo run --bin remote_server_cli -- refresh-dates
# See README.md link below
cargo run --bin remote_server_cli -- backup
cargo run --bin remote_server_cli -- restore -b D2024_08_22T05_05_16 -s

Config check tool

This tool is mostly intended for support, to confirm everything is configured correctly. It checks things like the database connection, and connecting to central servers for sync.

You can run the tool in either the cli mode or as a GUI. The postgreSQL and SQLite versions of each are shown below.

CLI

cargo run --bin remote_server_cli -- test-connection -u username -p password

When running the cli command, normal debug level logging to console is suppressed as this can get verbose. If you want to view all the log info, you can specify an -l parameter and specify the debug level:

cargo run --bin remote_server_cli -- test-connection -u username -p password -l info

GUI

cargo run --bin test_connection

or build and run the binary. Pass in --features postgres to run the postgres version.

Backup and Restore

Discovery

DNS-SD is available for all targets except for Android (for Android DNS-SD is toggled at runtime and is done in native java code). We also start another graphql server with initialisationStatus query endpoint, in http mode with permissive cors. This allows site information (initialised/site) to be presented during discovery.

Logging

By default, the server logs to console with a logging level of Info You can configure this, to log to a file, for example, with a rollover of log files based on file size. See the example.yaml file for the available options.

Directory structure

An overview of how files are organised in the server repo is shown below, with annotations as noted. The files have been split in this way, grouped in crates, partly to improve build times - as well as a logical organisation of grouped concerns.

server
├─ android (hoisting for the android implementation)
├─ cli
├─ configuration (runtime configuration files)
├─ data (contains reference data which can be used by the cli to initialise the database)
├─ docs
├─ graphql
│  ├─ batch_mutations (batch mutations are grouped here)
│  ├─ core (the loaders, filters, errors, pagination and test helpers)
│  ├─ general (mutations and queries which are shared or small enough to not require separate implementation)
│  ├─ types (each database table has its types defined in a file)
│  ├─ [data type]
│  │  ├─ src
│  │  │  ├─ lib.rs (queries are in here, if not in a separate file, as below)
│  │  │  ├─ [data type]_queries.rs
│  │  │  └─ mutations
│  │  └─ test_output
├─ report_builder
├─ repository
│  ├─ migrations
│  ├─ src
│  │  ├─ db_diesel (definitions for the database objects)
│  │  └─ mock
│  └─ test_output
├─ scripts
├─ server (includes the logging, front-end hosting, certificates, DNS-SD discovery, configuration)
├─ service (these functions provide an intermediary between GraphQL and the repository and houses most of the business logic)
├─ target
├─ test_output
├─ util
└─ windows (windows service hosting for the server)

The batch_mutations case is a special one - the crate is fairly slow to compile, so has been split out. There are also some dependency issues, as it combines other objects, such as invoice and invoice_line.