---
title: ops-sdk
description: The Rust SDK for services that run on ops. Boot time env, logs and traces, metrics, the healthcheck probe, status check-ins and translations.
order: 0
---

`ops-sdk` is the Rust crate a service on ops links to read its env file at boot, send its
logs and traces to ops, expose metrics, answer its container healthcheck, check in with
the status page and embed its translations. Each of these is a cargo feature, and **no
feature is on by default**, so a service compiles only what it asked for.

| feature | what it gives you |
| --- | --- |
| `env` | [Boot time env](env.md): the env file ops renders for the service, read in process |
| `observability` | [Logs, traces and metrics](observability.md): JSON logs, OTLP traces and a Prometheus registry |
| `probe` | [Healthcheck probe](probe.md): a dependency free `healthprobe` binary for images with no shell |
| `status` | [Status check-in](status.md): the dead man's switch for a job that must run on time |
| `i18n` | [Translations at build time](i18n.md): the project's translations pulled in `build.rs` |

## Install

The crate is published on the ops cargo registry, never on crates.io. Point cargo at the
registry once per machine, in `~/.cargo/config.toml`:

```toml
[registries.ops]
index = "sparse+https://ops.syncronet.dev/cargo/"

[registry]
global-credential-providers = ["cargo:token"]
```

Then sign cargo in with a token that may read packages, minted in the console:

```sh
cargo login --registry ops
```

The `global-credential-providers` line is required. The registry asks for a token on every
read, and without a credential provider cargo refuses each one with "authenticated
registries require a credential-provider to be configured". In CI, set
`CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS=cargo:token` and
`CARGO_REGISTRIES_OPS_TOKEN` instead of writing the file.

Add the crate with the features you want:

```toml
[dependencies]
ops-sdk = { version = "0.1", registry = "ops", features = ["env", "observability"] }
```

The Develop page of your project in the console prints the same blocks with your
registry's address filled in.

## The smallest service

```rust
use axum::{Router, middleware, routing::get};
use ops_sdk::observability::{init_tracing, metrics};

#[tokio::main]
async fn main() {
    // first: the env file ops rendered for this service, or .env on your machine
    ops_sdk::env::load().expect("the deploy env file");
    // then logs, and traces when OTEL_EXPORTER_OTLP_ENDPOINT is set
    init_tracing("web");

    let app = Router::new()
        .route("/", get(|| async { "hello from Rungholt" }))
        .route("/metrics", get(metrics::serve))
        .layer(middleware::from_fn(metrics::track));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}
```

## Requirements

Rust 1.85 or newer (edition 2024). The crate uses rustls for TLS and never OpenSSL, so it
adds no system library to your image.
