Skip to content

Operations

How to run FDS outside a development checkout: what it needs, every setting it reads, and how the database schema is managed. Nothing here is specific to any one installation.

Shape of a deployment

FDS is one container image serving HTTP on port 8000. It is stateless: everything it knows is in its database or in the object stores it brokers access to. A deployment therefore has three parts, and FDS provides only the first.

Part Provided by Notes
The API the FDS image Serves /v1/..., /health, and its OpenAPI explorer at /docs. Runs as an unprivileged user inside the container.
PostgreSQL you The only supported database. FDS applies its migrations on start.
An identity provider you FDS verifies tokens from the issuers listed in FDS_TRUSTED_IDPS. It never issues tokens and holds no client secret.

FDS speaks plain HTTP and does not terminate TLS; how requests reach it is your decision. Two things about its behaviour bear on that:

  • The identifiers in its JSON-LD output are built from the host the request arrived with, so the hostname clients use becomes part of the catalogue's public identity. Choose one you intend to keep.
  • When something in front of FDS rewrites requests, it believes X-Forwarded-* headers only from senders listed in FORWARDED_ALLOW_IPS. Setting FDS_BASE_URL is the more robust answer, because it stops FDS deriving its own address from requests at all.

Any web front end is a separate deployable that calls the API.

Container images

Two images are published to the GitHub Container Registry on every push to main and on every release tag:

Image What it is
ghcr.io/ukaea/fds The API.
ghcr.io/ukaea/fds-ui A brand-neutral reference web UI.

FDS serves JSON, never HTML. A catalogue people can browse, and the readable landing page a DOI has to resolve to, come from a web front end running alongside FDS. The reference UI is published so that a deployment has one without building it first, and so there is a working example to base your own on. Only a deployment serving machines alone can do without it.

Each carries four kinds of tag:

Tag Moves? Use it for
sha-abc1234 never Deployments. It names one build and always will.
1.4.0 never Deployments pinned to a release.
latest with each release tag Trying the newest release out.
main with every merge Development only.

Pin a deployment to sha-<short> or a version, never to main or latest: a rollback is only possible if the tag you rolled back from still names the build you were running.

Database

FDS runs on PostgreSQL and has no other backend. Point it at one with FDS_DB_HOST and friends, or give it a complete URL in FDS_DB_URL.

Schema migrations

The schema is created and changed only by the Alembic migrations shipped with the code, in alembic/versions/. A migration describes how the table layout changes from one release to the next. It carries no data and nothing about any particular installation; the connection comes from the environment at run time. The only per-installation state is a one-row alembic_version table recording which migration that database has reached.

The image runs alembic upgrade head before starting the server, so a new database is built and an existing one brought up to date on every start. To run it separately, for example before swapping in a new image:

alembic upgrade head

Every migration is reversible. Rolling back to an older release that expects an older schema means downgrading first, then deploying the older image:

alembic downgrade <revision>

Back up the database before either operation on data you care about.

Moving to a new hostname

FDS stores nothing about its own address. The URLs in its responses, including every JSON-LD @id, are built from the incoming request's host, and a record created locally carries no origin (only records ingested from another catalogue do). So a change of hostname needs no change to the database. What it does need is outside FDS:

  • Records held elsewhere. Harvesters, other catalogues and citations hold the old URLs. Keeping the old hostname answering with redirects for as long as you can is the usual remedy.
  • DOIs. Update the landing URL on each DOI record at the registrar; that indirection is what a DOI is for.

The same reasoning applies in the other direction. When ingesting records from another catalogue, set their origin to the most persistent identifier that catalogue has (a re3data record, a DOI, an identifier it advertises in its own DCAT catalogue) and use the same value every time; its URL is the fallback, and it goes stale if that catalogue moves.

Settings

Every setting is an environment variable. .env.example in the repository lists them with their defaults.

Application

Setting Effect
FDS_APP_NAME Service name in the OpenAPI document and in traces. Default fds.
FDS_BASE_URL The address this service is reached at, which every published identifier is a path under. Set this in any deployment. Empty (the default) derives it from each request, which is correct only when FDS is reached directly, as in development. See Identifiers.
FDS_ENVIRONMENT dev or prod. Selects the default log format.
FDS_DEBUG Enables debug behaviour. Off in production.
FDS_LOG_FORMAT, FDS_LOG_LEVEL See Logging.

Database

Setting Effect
FDS_DB_HOST PostgreSQL host. Default localhost.
FDS_DB_PORT PostgreSQL port. Default 5432.
FDS_DB_NAME Database name. Default fds.
FDS_DB_USER, FDS_DB_PASSWORD Credentials.
FDS_DB_URL A complete SQLAlchemy URL, which overrides the four settings above.

Identity

Setting Effect
FDS_TRUSTED_IDPS JSON list of issuers whose tokens are accepted, each with the scopes it may grant, and optionally where its keys come from. See Access Control. Empty means no token is accepted and only public metadata is readable.
FDS_OIDC_AUDIENCE The aud claim a token must carry.

Storage

Setting Effect
FDS_STORAGE_PROVIDERS JSON list of object stores FDS can vend credentials for. Empty (the default) vends none. See Storage providers.
FDS_CREDENTIAL_TOKEN_DURATION Lifetime in seconds of vended credentials. Default 3600.

Storage providers

Each entry describes one store. A distribution gets credentials from the entry whose type matches its URL's scheme and whose endpoint_url equals its own. FDS refuses to start if two entries share a type and endpoint_url, since only the first could ever be chosen.

type Schemes Fields Credentials FDS signs with
s3 s3:// endpoint_url (what clients connect to, null for AWS), sts_endpoint_url (what FDS calls, if different), region, sts_role_arn, and optionally sts_access_key_id / sts_secret_access_key The two keys if given, otherwise the standard AWS credential chain
gcs gs://, gcs:// endpoint_url (optional) Google Application Default Credentials, such as a key file named by GOOGLE_APPLICATION_CREDENTIALS
azure az://, abfs://, abfss:// storage_account, endpoint_url (optional) Azure's default credential chain, such as environment variables or a managed identity

type is required in every entry, s3 included. For gcs and azure, endpoint_url only matches distributions: FDS never calls it. An Azure URL that names its account, as in abfs://container@account.dfs.core.windows.net/path, gets credentials only when that account is the provider's storage_account.

[
  {
    "type": "s3",
    "endpoint_url": "https://s3.echo.stfc.ac.uk",
    "region": "us-east-1",
    "sts_role_arn": "arn:aws:iam::123456789012:role/fds"
  },
  {"type": "gcs", "endpoint_url": null}
]

Serving

Read by the web server and the tracing library rather than by FDS itself.

Setting Effect
WEB_CONCURRENCY Number of worker processes. Default one. See Logging for what several workers mean for the log stream.
FORWARDED_ALLOW_IPS Senders whose X-Forwarded-* headers are trusted. Default 127.0.0.1; * when nothing but the intended front can reach the container.
OTEL_EXPORTER_OTLP_ENDPOINT Where to send traces. Unset means traces are not exported. Other OTEL_* variables are honoured as documented by OpenTelemetry.

Health

GET /health returns 200 with the running version when the process is up. It does not touch the database; use it for container health checks and uptime probes, and monitor the database directly.