12.2 What goes where: source control, deployment and appsettings
An application on Relatude.DB is configured from four places: the code, the settings files
in relatude.settings/ (SHARED), the configuration of each environment (appsettings*.json,
environment variables, secrets), and the data folder of each installation (THIS SERVER). Three
rules decide what goes where:
- What describes the application is committed to source control and deployed with the code.
- What an installation changed or recorded stays with that installation. It is never committed, and a deployment neither carries it nor overwrites it.
- What differs between environments comes from configuration, and so does every secret.
In short: commit relatude.settings/. Never commit relatude.data/ or relatude.data.temp/,
and exclude both from the project file so that a publish does not deploy them either
(Publishing).
The folders. With the default ServerOptions, a project looks like this. The content root is the
project folder while you develop, and the folder the application is deployed to on a server.
MyApp/ content root and root data folder
├── Program.cs, Models/, … the code committed
├── appsettings.json configuration for every environment committed, no secrets
├── appsettings.Development.json what Development changes committed
├── appsettings.Production.json what Production changes committed, no secrets
├── relatude.settings/ SHARED: what the application says committed, deployed
│ ├── relatude.db.json databases, storage, index engines, model sources
│ ├── <FileKey>.json custom settings
│ ├── datamodel.json datamodel overrides (§3.1)
│ ├── logs/ log definitions, one <key>.json per log (§32.1)
│ ├── graphql/ GraphQL endpoints, one <name>.json each (§33.2)
│ └── <ShortName>/ the same, per database with a short name
├── relatude.data/ THIS SERVER: this installation never committed or deployed
│ ├── data/ state/ backup/ logs/ … transaction log, state, backups, log entries, indexes, files
│ ├── overrides/ what the admin UI changed here
│ │ ├── relatude.db.overrides.json settings (default database only)
│ │ ├── <FileKey>.overrides.json custom settings
│ │ ├── datamodel.overrides.json datamodel overrides
│ │ └── logs/ graphql/ log definitions and endpoints added, changed or removed here
│ ├── datamodels/ the model editor's draft and model history
│ ├── modelsources/ model files of RuntimeTypes sources without a Filepath
│ └── installation/ this installation's id
└── relatude.data.temp/ scratch, emptied at every start never committed or deployedOther databases keep the folders from data/ to datamodels/ in their own storage.
relatude.settings/ sits in the root data folder, so ServerOptions.DefaultDataFolderPath moves it
too. A database's SHARED files are found by its short name, so set each database's ShortName in
relatude.db.json, where every installation reads it (§12.1).
At every start, THIS SERVER is merged over SHARED. Logs and GraphQL endpoints are replaced whole: a definition on THIS SERVER replaces the SHARED one with the same key, and one removed on THIS SERVER is gone on that installation only.
While you develop
dotnet run and Visual Studio start the application with the project folder as its content root. So
the relatude.settings/ the application reads is the folder in source control, and what the admin UI
writes into it is ready to commit:
- Settings changed on the settings pages go to the overrides file in
relatude.data/, outside source control. That is the place to try a value out. To keep a change, choose Move to shared → Move into relatude.db.json on the settings page, and commit the file. - Model changes in the model editor are written into the model source where it can be written:
the model files of a
RuntimeTypessource, or the generated C# files of aCompiledTypessource (rebuild and restart to see them). Otherwise they are saved as overrides on THIS SERVER, and Move to shared in the model editor moves them intorelatude.settings/…/datamodel.json. Commit that. - Log definitions and GraphQL endpoints made in the admin UI go to
relatude.data/overrides/. Move to shared on the Logs and API pages moves them intorelatude.settings/…/logs/and…/graphql/. Commit those. - A
RuntimeTypessource without aFilepathkeeps its model files inrelatude.data/modelsources/, inside the data folder. For a model that belongs to the application, give the source aFilepathin the project, such as"Models/Json", and commit that folder.
The .gitignore needs only two entries for Relatude. relatude.settings/ is committed:
relatude.data/
relatude.data.temp/A complete .gitignore for a
typical project, with build output, client builds and secret files too, is in the documentation. Put
it in the root of the repository; its patterns match at any depth. The project types of
relatude new come with their own. A .gitignore does not stop a publish from copying the data
folder (below).
Secrets do not go into relatude.db.json. relatude new and relatude init write a random
TokenEncryptionSecret into the file, and the admin password when given --password, so you can log
in to a new project at once. Before you share the repository, move both into user secrets
(below) and remove them from the file.
Publishing
The Web SDK copies every .json file in the project folder to the publish folder. That is how
relatude.settings/ and the appsettings files reach a server, but it would also deploy your
machine's data folder over the server's. So the project file leaves the data folder out, as the
project types of relatude new do:
<PropertyGroup>
<DefaultItemExcludes>$(DefaultItemExcludes);relatude.data\**;relatude.data.temp\**</DefaultItemExcludes>
</PropertyGroup>Check the publish folder before the first deployment: relatude.settings/relatude.db.json must be
there, and relatude.data/ must not. A server that finds no settings file creates one that uses the
bundled demo model. So a database full of Relatude.DB.Demo.Models types means the deployment left
relatude.settings/ behind.
On a server
A deployment leaves the data alone. It replaces the code, relatude.settings/ and the appsettings
files, but nothing in the data folder, which is relatude.data/ in the application's folder by
default. That works on a virtual machine, under IIS or on Azure App Service, as long as the deployment
never deletes files it did not put there: turn off Remove additional files at destination, and keep
relatude.data/ out of an rsync --delete. Two other setups are common:
- In a container, mount a volume at
relatude.data/below the content root. The image carries the code andrelatude.settings/, and the volume carries the installation. - On Azure Blob storage (an
IOSettingsentry of typeAzureBlobStorageforIoDatabase), the database and itsoverrides/live in the blob container, and the application's folder holds only what was deployed. Use this with deployment slots: a swap exchanges the slots' folders, so a database kept in the folder would change places with the code.
Admin UI changes stay with the installation. Settings, datamodel overrides, log definitions and GraphQL endpoints changed on a server are kept on THIS SERVER, in the data folder. They survive every deployment, and each deployment's SHARED files still apply beneath them.
A move to SHARED on a server does not last. Every Move to shared writes into
relatude.settings/, and so does activating a model change in a RuntimeTypes source whose
Filepath is in the application's folder. On a server those files are the deployed copy, which the
next deployment (or the next container) replaces. The moved entries have left the overrides file too,
so they are lost. The dialogs warn about this outside the Development environment. Bring a change into
the application through source control instead:
| Changed on a server | To make it part of the application |
|---|---|
| A setting | Copy the value from the Move to shared list into relatude.db.json in source control, and deploy. Once the deployed file agrees, the next save drops the entry. |
| A datamodel override | In the model editor's Move to shared dialog, Download shared file gives a datamodel.json with the chosen overrides, without changing the server. Commit it and deploy. |
| A log definition | The log's Definition view shows its file. Copy it into relatude.settings/…/logs/ and deploy. Entries stay in the data folder, so a definition with the same key picks them up. |
| A GraphQL endpoint | Copy the endpoint's file into relatude.settings/…/graphql/ and deploy, or map the endpoint in code. |
A model in a RuntimeTypes source |
Copy the files from the source's Filepath into source control before the next deployment, or make the change in development instead. |
A restored data folder brings its changes along. Production's relatude.data/ restored on a test
server brings production's settings, datamodel overrides, log definitions and GraphQL endpoints with
it, on THIS SERVER. Look through the Move to shared dialogs after such a restore.
appsettings, environment variables and secrets
The RelatudeDB configuration section (above) lets one
relatude.db.json serve every environment. ASP.NET reads configuration in this order, a later source
winning: appsettings.json, appsettings.{Environment}.json, user secrets (in Development only),
environment variables, the command line. The environment is Production unless
ASPNETCORE_ENVIRONMENT says otherwise; dotnet run and Visual Studio set Development.
A split that works:
| Where | What goes there |
|---|---|
relatude.db.json |
What every environment shares: the databases and their ids, storage backends without connection strings, file stores, index engines, model sources, engine defaults. |
appsettings.json |
Relatude values every environment shares that you prefer to keep with the rest of the configuration. Often nothing. |
appsettings.{Environment}.json |
What one environment does differently: cache sizes, backups, AllowMasterLoginOutsideLocalhost, another blob container. |
| User secrets | A developer machine's secrets: the admin password, TokenEncryptionSecret, API keys, a storage connection string. They live in your user profile, outside the project folder. |
| Environment variables, a vault | A server's secrets: App Service application settings, a container's environment, Key Vault references. |
// appsettings.Production.json: committed, no secrets
{
"RelatudeDB": {
"AllowMasterLoginOutsideLocalhost": true,
"ContainerSettings": [
{ "Id": "8f6b…", "LocalSettings": { "NodeCacheSizeGb": 4, "AutoBackUp": true } }
]
}
}# a developer's machine ("init" adds a UserSecretsId to the project file the first time)
dotnet user-secrets init
dotnet user-secrets set "RelatudeDB:MasterPassword" "…"
dotnet user-secrets set "RelatudeDB:TokenEncryptionSecret" "…"# a server: environment variables, or application settings on Azure App Service
RelatudeDB__MasterPassword=…
RelatudeDB__TokenEncryptionSecret=…
RelatudeDB__ContainerSettings__0__Id=8f6b…
RelatudeDB__ContainerSettings__0__IOSettings__0__Id=9d8c…
RelatudeDB__ContainerSettings__0__IOSettings__0__BlobConnectionString=…Environment variables separate the levels with __, where user secrets use :. The numbers are
positions in the configuration's own lists. Give each element's Id beside them, as above, and the
element is matched by id to the one in relatude.db.json, not by position. Then reordering the file
never sends a connection string to the wrong backend.
A few things to know:
- An empty value is a value.
"ApiKey": ""in an appsettings file blanks the setting. Leave the key out instead; a//comment can say where the real value comes from. - Configuration cannot remove anything or set a value to null. Which databases and storage
backends exist belongs in
relatude.db.json. - The command line tool reads the same files.
relatude settings --environment Productionshows the settings a Production server would run with, from the appsettings files and the shell's environment variables, but not user secrets. It reports each secret as set or not set, never its value (§31).