{"id":26089059,"url":"https://github.com/metaswitch/rpmoci","last_synced_at":"2025-04-12T05:09:43.199Z","repository":{"id":65976139,"uuid":"536287092","full_name":"Metaswitch/rpmoci","owner":"Metaswitch","description":"OCI container builder for RPM-based Linux distributions","archived":false,"fork":false,"pushed_at":"2025-02-17T14:42:51.000Z","size":404,"stargazers_count":15,"open_issues_count":9,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-02-17T15:30:30.602Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Metaswitch.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-09-13T19:52:04.000Z","updated_at":"2025-02-17T14:41:59.000Z","dependencies_parsed_at":"2023-02-19T19:20:25.358Z","dependency_job_id":"1e53a7e9-f039-47da-8ad8-3675f591c6d6","html_url":"https://github.com/Metaswitch/rpmoci","commit_stats":null,"previous_names":["metaswitch/rpmoci"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Metaswitch%2Frpmoci","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Metaswitch%2Frpmoci/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Metaswitch%2Frpmoci/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Metaswitch%2Frpmoci/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Metaswitch","download_url":"https://codeload.github.com/Metaswitch/rpmoci/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248519549,"owners_count":21117761,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-09T08:58:06.161Z","updated_at":"2025-04-12T05:09:43.162Z","avatar_url":"https://github.com/Metaswitch.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rpmoci\n\nrpmoci builds OCI container images from RPM packages, using [DNF](https://github.com/rpm-software-management/dnf). It's essentially a containerization wrapper around `dnf install --installroot=/some/rootfs PACKAGE [PACKAGE ...]`.\n\nrpmoci features:\n\n - **deterministic** rpmoci locks RPM dependencies using the package file/lockfile paradigm of bundler/cargo etc and can produce reproducible images with identical digests.\n - **unprivileged** rpmoci can build images in environments without access to a container runtime, and can also run in a user namespace.\n - **small** rpmoci images are built solely from the RPMs you request and their dependencies, so don't contain unnecessary dependencies.\n\nrpmoci is a good fit for containerizing applications - you package your application as an RPM, and then use rpmoci to build a minimal container image from that RPM.\n\nThe design of rpmoci is influenced by [apko](https://github.com/chainguard-dev/apko) and [distroless](https://github.com/GoogleContainerTools/distroless) tooling.\nrpmoci is also similar to a smaller [`rpm-ostree compose image`](https://coreos.github.io/rpm-ostree/container/#creating-base-images), with a focus on building microservices.\n\n\n## Installing\n\nrpmoci has a runtime dependency on dnf, so requires a Linux distribution with dnf support.\n\nrpmoci is available to download from crates.io, so you'll need a Rust toolchain. You also need to install the sqlite, python3 and openssl development packages (e.g `sqlite-devel`, `python3-devel` and `openssl-devel` on Fedora and RHEL derivatives). \n\nThen install rpmoci via cargo:\n```bash\ncargo install rpmoci\n```\n\n## Building\nPer the above, you'll need dnf, Rust, python3-devel and openssl-devel installed.\n\n```bash\ncargo build\n```\n\n### Rootless\nrpmoci can create images as a non-root user using [user namespaces](https://man7.org/linux/man-pages/man7/user_namespaces.7.html).\n\n```bash\n$ unshare --map-auto --map-root-user --user rpmoci build --image foo --tag bar\n```\n\n## Getting started\nYou need to create an rpmoci.toml file. An example is:\n\n```toml\n[contents] # specifies the RPMs that comprise the image\nrepositories = [ \"mariner-official-base\" ]\npackages = [\n  \"tini\"\n]\n\n[image] # specifies image configuration such as entrypoint, ports, cmd, etc.\nentrypoint = [ \"tini\", \"--\" ]\n```\n\nThis configures rpmoci to install `tini` and its dependencies from the mariner-official-base repository, and configures the image entrypoint to use `tini`.\n\n\nThis can then be built into an image:\n```bash\nsudo rpmoci build --image tini --tag my-first-rpmoci-image\n```\n\nThe image will be created in an OCI layout directory called `tini`.\nrpmoci doesn't handle image distribution - users are expected to use tools like [oras](https://oras.land/) or [skopeo](https://github.com/containers/skopeo) to push the image to a registry.\n\nA lockfile, `rpmoci.lock`, will be created so you can re-run the build later and get the same packages.\n*assuming they still exist in the specified repository... rpmoci supports vendoring RPMs so you can repeat locked builds without relying on that*\n\n## Reference\n### Package Specification\n#### Repository configuration\nThe repository section defines where RPMs are sourced from.\n\nIn the getting started example, the repository was specified by its repo id on the running system.\nIt is also possible to fully specify the repository in `rpmoci.toml`, if you want to create a portable `rpmoci.toml` that can say, build the same image when running on Fedora/Ubuntu/Mariner.\n\nRepositories can be specified via their base URL\n```toml\n[contents]\nrepositories = [\"https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64\"]\n```\n\nor defined with additional configuration options in the package manifest file (`rpmoci.toml` by default, can be specified via `-f FILE` on CLI)\n```toml\n[[contents.repositories]]\nurl = \"https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64/\"\noptions = { includepkgs = \"foo,bar\" }\n```\n\nBy default the `gpgcheck` and `sslverify` are enabled - these can be disabled via the `options` field.\n\nAll system repos are ignored, other than those explicitly specified via repo id.\ndnf plugins are supported, but rpmoci doesn't support specifying plugin configuration.\n\n#### Package configuration\n\nPackage specifications are added under the `contents.packages` key. Both local and remote packages are supported\n\n```toml\n[contents]\nrepositories = [\"https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64\"]\npackages = [\n  \"postgreqsl\", # a package from the above repository\n  \"path/to/local.rpm\", # a local RPM\n]\n```\n\n#### Documentation file\n\nWhether or not documentation files are included in the produced containers can be specified via the `content.docs` boolean field.\nBy default documentation files are not included, optimizing for image size.\n\n\n#### GPG key configuration\nGPG keys can be configued via the repository options or the `gpgkeys` field\n\n```toml\n[contents]\nrepositories = [\"https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64\"]\ngpgkeys = [\n  \"https://raw.githubusercontent.com/microsoft/CBL-Mariner/2.0/SPECS/mariner-repos/MICROSOFT-RPM-GPG-KEY\"\n]\npackages = [\n  \"postgresql\"\n]\n```\n\nWhen building images the package signatures will be verified using the configured GPG keys, except for local packages or packages from repositories where `gpgcheck` has explicitly been disabled.\n\n#### Authenticated RPM repositories\nTo use a repository that requires HTTP basic authentication, specify an `id` for the repository in the toml file,\nand define the environment variables `RPMOCI_\u003cid\u003e_HTTP_USERNAME` and `RPMOCI_\u003cid\u003e_HTTP_PASSWORD` to be the HTTP authentication credentials, where `\u003cid\u003e` is the uppercased repo id.\n\nE.g with the following configuration you would need to define the environment variables `RPMOCI_FOO_HTTP_USERNAME` and `RPMOCI_FOO_HTTP_PASSWORD`:\n```toml\n[[contents.repositories]]\nurl = \"https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64/\"\nid = \"foo\"\n```\n\n### Image configuration\n\nAdditional [image configuration](https://github.com/opencontainers/image-spec/blob/main/config.md#properties) can be specified under the `image` key:\n\n```toml\n[contents]\nrepositories = [\"https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64\"]\ngpgkeys = [\n  \"https://raw.githubusercontent.com/microsoft/CBL-Mariner/2.0/SPECS/mariner-repos/MICROSOFT-RPM-GPG-KEY\"\n]\npackages = [\n  \"postgresql\"\n]\n[image]\nentrypoint = [\"tini\", \"--\"]\ncmd = [ \"foo\" ]\nexposed_ports = [\"8080/tcp\"]\n\n[image.envs]\nRUST_BACKTRACE = \"1\"\nRUST_LOG = \"hyper=info\"\n```\n\nThe `config` section of the OCI image spec, linked above, maps to the image section in `rpmoci.toml`.\nFor example to specify image labels you can use the `image.labels` section and to specify image environment variables use `image.envs`.\n\nThe PATH environment variable is set to `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` by default, but can be overridden via the `image.envs` field.\n\n#### /etc/os-release\n\nWhether `/etc/os-release` is automatically included as a dependency during resolution, hence installed in the produced image, can be specified via the `content.os_release` boolean field.\nThis enables SBOM and vulnerability scanning tools to better determine the provenance of packages within the image.\nBy default this field is enabled.\n\n*The /etc/os-release file can also be included by adding the distro's `\u003cdistro\u003e-release` package to the packages array: this field exists to ensure the /etc/os-release file is included by default.*\n\n#### Weak dependencies\n\nrpmoci does not install [weak dependencies](https://docs.fedoraproject.org/en-US/packaging-guidelines/WeakDependencies/#:~:text=Weak%20dependencies%20should%20be%20used%20where%20possible%20to,require%20the%20full%20feature%20set%20of%20the%20package.), optimizing for small container image sizes.\n\n### Image building\n\nRunning `rpmoci build --image foo --tag bar` will build a container image in OCI format.\n\n```bash\n$ rpmoci build --image foo --tag bar\n...\n$ cat foo/index.json | jq\n{\n  \"schemaVersion\": 2,\n  \"manifests\": [\n    {\n      \"mediaType\": \"application/vnd.oci.image.manifest.v1+json\",\n      \"digest\": \"sha256:1ad8cc1866d359e4e2ecb37fcc96759815540f06cb468811dcb9b8aac51da90d\",\n      \"size\": 350,\n      \"annotations\": {\n        \"org.opencontainers.image.ref.name\": \"bar\"\n      }\n    }\n  ]\n}\n```\n\nThis image can then be copied using OCI tools such as skopeo or oras. E.g to copy to a local docker daemon:\n```bash\n$ skopeo copy oci:foo:bar docker-daemon:foo:bar\nGetting image source signatures\nCopying blob 77b582c1f09c done\nCopying config 577bea913f done\nWriting manifest to image destination\nStoring signatures\n```\n\n#### Lockfiles\n\nrpmoci uses DNF to produce a lockfile of the build. This can be used to subsequently repeat the build with `rpmoci build --locked`.\n\nA lockfile can be created or updated by running `rpmoci update`:\n\n```bash\n$ rpmoci update\nAdding filesystem 1.1-10.cm2\nAdding grep 3.7-2.cm2\nAdding openssl 1.1.1k-17.cm2\nAdding libgcc 11.2.0-2.cm2\nAdding postgresql 14.2-2.cm2\nAdding libxml2 2.9.14-1.cm2\nAdding ncurses-libs 6.3-1.cm2\nAdding pcre 8.45-2.cm2\nAdding pcre-libs 8.45-2.cm2\nAdding glibc 2.35-2.cm2\nAdding bash 5.1.8-1.cm2\nAdding libsepol 3.2-2.cm2\nAdding libcap 2.60-1.cm2\nAdding krb5 1.19.3-1.cm2\nAdding openldap 2.4.57-7.cm2\nAdding coreutils 8.32-3.cm2\nAdding postgresql-libs 14.2-2.cm2\nAdding libselinux 3.2-1.cm2\nAdding openssl-libs 1.1.1k-17.cm2\nAdding readline 8.1-1.cm2\nAdding tzdata 2022a-1.cm2\nAdding xz-libs 5.2.5-1.cm2\nAdding libstdc++ 11.2.0-2.cm2\nAdding zlib 1.2.12-1.cm2\nAdding e2fsprogs-libs 1.46.5-1.cm2\nAdding gmp 6.2.1-2.cm2\nAdding bzip2-libs 1.0.8-1.cm2\n```\n\n#### Reproducible builds\nrpmoci can produce bitwise reproducible container image builds, assuming that the RPMs can be reproducibly installed (an rpmoci build won't be reproducible if it involves RPMs that have unreproducible post-install scripts for example).\nrpmoci attempts to remove sources of non-determinism from the container image, and respects the [SOURCE_DATE_EPOCH](https://reproducible-builds.org/docs/source-date-epoch/) environment variable.\n\nWhen SOURCE_DATE_EPOCH is not set, the image creation time in the OCI image config is set to the current time. In this scenario rpmoci still removes non-deteministic data from the image, and the build can later be reproduced by setting SOURCE_DATE_EPOCH to the creation time of the image (by converting the timestamp in the image config to seconds since unix epoch). \n\nThis feature is only been tested on Mariner Linux, but should work when rpmoci is run on any Linux distribution that writes the rpmdb as a sqlite database to `/var/lib/rpm/rpmdb.sqlite`.\n\n#### Vendoring\n\nRPMs can be vendored to a folder using `rpmoci vendor`. A vendor folder can be used during a build to avoid contacting package repositories.\n\n```bash\n$ rpmoci vendor --out-dir vendor\n$ ls vendor\nls vendor\n031e779a7ce198662c5b266d7b0dfc9eece9c0c888a657b6a9bb7731df0096d0.rpm  8ea3d75dbb48fa12eacf732af89a600bd97709b55f88d98fe129c13ab254de95.rpm\n...\n$ rpmoci build --image foo --tag bar --vendor-dir vendor\n```\n*Vendor directories from different invocations of `rpmoci vendor` should be kept isolated, as rpmoci currently attempts to install all RPMs from the vendor directory.*\n\n\n#### SBOM support\nrpmoci doesn't have native SBOM support, but because it just uses standard OS package functionality SBOM generators like trivy and syft can be used to generate SBOMs for the produced images.\n\n## Developing\n\nrpmoci is written in Rust and currently resolves RPMs using DNF via an embedded Python module.\n\nIt has buildtime dependencies on `python3-devel` and `openssl-devel`.\n\nAfter checking out the project you can do\n```bash\ncargo run\n```\nto run it, or build an RPM using [cargo-generate-rpm](https://github.com/cat-in-136/cargo-generate-rpm):\n```bash\ncargo generate-rpm\n```\n\n### Testing\n\nThe tests are run via `cargo test`. The integration tests in `tests/it.rs` run `rpmoci build`,\nso must be run either as root, or with user namespace support setup.\n\nThe tests use [test-temp-dir](https://docs.rs/crate/test-temp-dir/latest), so you can set \nthe `TEST_TEMP_RETAIN` environment variable to `1` so that the test directories are kept around for debugging in `\u003cCARGO_TARGET_DIR\u003e/tests`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetaswitch%2Frpmoci","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmetaswitch%2Frpmoci","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetaswitch%2Frpmoci/lists"}