{"id":47808863,"url":"https://github.com/pelagos-containers/pelagos","last_synced_at":"2026-06-14T04:00:59.138Z","repository":{"id":339044385,"uuid":"372842869","full_name":"pelagos-containers/pelagos","owner":"pelagos-containers","description":"Daemonless Linux container runtime with a Lisp scripting interface — security-by-default, library API, full networking stack, OCI images, and multi-service orchestration","archived":false,"fork":false,"pushed_at":"2026-06-06T02:15:38.000Z","size":242708,"stargazers_count":9,"open_issues_count":13,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-06-06T02:23:57.007Z","etag":null,"topics":["containers","daemonless","linux","lisp","namespaces","orchestration","rust","seccomp"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pelagos-containers.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":"docs/SECURITY.md","support":null,"governance":null,"roadmap":"docs/ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-06-01T13:37:19.000Z","updated_at":"2026-06-06T01:37:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pelagos-containers/pelagos","commit_stats":null,"previous_names":["skeptomai/remora","skeptomai/pelagos","pelagos-containers/pelagos"],"tags_count":116,"template":false,"template_full_name":null,"purl":"pkg:github/pelagos-containers/pelagos","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pelagos-containers%2Fpelagos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pelagos-containers%2Fpelagos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pelagos-containers%2Fpelagos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pelagos-containers%2Fpelagos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pelagos-containers","download_url":"https://codeload.github.com/pelagos-containers/pelagos/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pelagos-containers%2Fpelagos/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34308622,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["containers","daemonless","linux","lisp","namespaces","orchestration","rust","seccomp"],"created_at":"2026-04-03T18:00:35.459Z","updated_at":"2026-06-14T04:00:59.118Z","avatar_url":"https://github.com/pelagos-containers.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pelagos\n\n[![CI](https://github.com/pelagos-containers/pelagos/actions/workflows/ci.yml/badge.svg)](https://github.com/pelagos-containers/pelagos/actions/workflows/ci.yml)\n\n**Pelagos** is a daemonless Linux container runtime written in Rust. It can run a\nsingle container or orchestrate a multi-service stack — and its primary interface\nis a Lisp scripting language, not YAML.\n\nThe `.reml` scripting layer lets you express things that declarative config cannot:\nrun a migration before the app starts, wait for a port to be ready, react to\nfailure with cleanup logic, or build a dependency graph that executes in parallel.\nA runtime that is programmable from first principles.\n\nPelagos is also an embeddable Rust library, making it possible to add container\nisolation directly to your own programs without spawning a daemon or shelling out\nto Docker.\n\n**[User Guide](docs/USER_GUIDE.md)** — full CLI reference, networking, storage,\nsecurity, scripting, and more.\n\n---\n\n## What makes it different\n\n| | Pelagos | Docker | runc |\n|--|--------|--------|------|\n| Daemon required | ❌ | ✅ | ❌ |\n| Library API | ✅ | ❌ | ❌ |\n| Config language | Lisp (`.reml`) | YAML | JSON |\n| Imperative scripting | ✅ full language | ❌ | ❌ |\n| Security-by-default | ✅ all containers | opt-in | opt-in |\n| Rootless networking | ✅ pasta | ✅ | limited |\n| Linux + Wasm, one runtime | ✅ | ❌ | ❌ |\n\n**Security-by-default** means every container gets seccomp-BPF filtering,\nall capabilities dropped, no-new-privileges, masked kernel paths, and PID/UTS/IPC\nnamespace isolation — without any flags. Services that need specific capabilities\nopt back in with `:cap-add`.\n\n**Linux and Wasm, unified.** No other general-purpose container runtime handles\nWebAssembly natively — runc, crun, and youki treat `.wasm` files as opaque\nexecutables and fail at `exec()`. Wasm-native runtimes (runwasi, Spin, WasmEdge\nshim) go the other direction: Wasm only, no Linux OCI containers. Pelagos is the\nonly runtime where both workload types share one CLI, one image store, one compose\nformat, and one node: `pelagos run alpine /bin/sh` and\n`pelagos run ghcr.io/example/my-app:latest` work the same way whether the image\ncontains an ELF binary or a `.wasm` module. See [`docs/WASM_SUPPORT.md`](docs/WASM_SUPPORT.md).\n\n---\n\n## The `.reml` scripting interface\n\nPelagos compose files are Lisp programs, not config schemas. A minimal stack:\n\n```lisp\n(define-service svc-db \"db\"\n  :image \"postgres:16\"\n  :env   (\"POSTGRES_PASSWORD\" \"secret\"))\n\n(define-service svc-app \"app\"\n  :image      \"myapp:latest\"\n  :depends-on \"db\" 5432)\n\n(compose-up\n  (compose svc-db svc-app))\n```\n\nBut when you need more than ordering, the full language is available:\n\n```lisp\n; Start the database and wait for it\n(define db (container-start svc-db))\n(await-port \"localhost\" 5432 :timeout 60)\n\n; Run migrations — abort the whole deploy if they fail\n(define exit-code (container-run svc-migrate))\n(unless (zero? exit-code)\n  (error \"migrations failed — aborting\"))\n\n; Start the app, clean up on any exit\n(with-cleanup (lambda (result)\n                (container-stop db)\n                (logf \"deploy result: ~a\" result))\n  (container-wait (container-start svc-app)))\n```\n\nThe same interpreter supports a **futures graph** for parallel execution:\n\n```lisp\n(define-nodes\n  (db    svc-db)\n  (cache svc-cache))\n\n(define-then db-url db (h)\n  (format \"postgres://app:secret@~a/appdb\" (container-ip h)))\n\n(define-run :parallel\n  (app-handle app)\n  (db-url     db-url))\n```\n\nSee [`docs/REML_EXECUTOR_MODEL.md`](docs/REML_EXECUTOR_MODEL.md) for the full\nscripting reference.\n\n---\n\n## Features\n\n### Isolation\n- **Namespaces:** UTS, Mount, IPC, Network, User, PID, Cgroup\n- **Filesystem:** chroot, pivot_root, automatic /proc /sys /dev mounts\n- **Security defaults:** seccomp-BPF + all capabilities dropped + no-new-privileges\n  + masked paths applied to every container unconditionally\n\n### Security\n- **Seccomp-BPF:** Docker's default profile via pure-Rust `seccompiler`\n- **Capability management:** all caps dropped by default; `:cap-add` restores specific ones\n- **No-new-privileges:** `PR_SET_NO_NEW_PRIVS` blocks setuid/setgid escalation\n- **Read-only rootfs:** `MS_RDONLY` remount makes the filesystem immutable\n- **Masked paths:** `/proc/kcore`, `/sys/firmware`, and others hidden\n- **Landlock LSM:** per-path filesystem rules via Linux 5.13+ kernel interface\n- **Structural TOCTOU immunity:** Pelagos uses a single-threaded `pre_exec` hook and\n  never re-execs itself — the architecture that drives the November 2025 runc CVE\n  cluster (CVE-2025-31133, CVE-2025-52565, CVE-2025-52881) does not exist in Pelagos.\n  See [docs/SECURITY.md](docs/SECURITY.md) for details.\n\n### Networking\n- **Loopback:** isolated NET namespace, `lo` only\n- **Bridge:** veth pair + named bridge, IPAM, DNS service discovery\n- **NAT:** nftables MASQUERADE, reference-counted across containers\n- **Port mapping:** TCP DNAT via nftables + userspace proxy for localhost\n- **Named networks:** user-defined bridge networks with custom subnets\n- **Multi-network:** attach containers to multiple networks simultaneously\n- **DNS service discovery:** dual-backend (built-in daemon or dnsmasq), automatic\n  container name resolution on bridge networks\n- **Pasta:** full internet access without root via [pasta](https://passt.top/passt/about/)\n\n### Resource Management\n- **rlimits:** memory, CPU time, file descriptors, process count\n- **Cgroups v2:** memory hard limit, CPU weight, CPU quota, PID limit\n- **Resource stats:** `child.resource_stats()` reads live cgroup counters\n\n### Filesystem\n- **Bind mounts:** `with_bind_mount()` (RW) and `with_bind_mount_ro()` (RO)\n- **tmpfs:** writable scratch space inside a read-only rootfs\n- **Named volumes:** persisted storage, scoped per compose project\n- **Overlay filesystem:** copy-on-write layered rootfs via overlayfs\n\n### OCI Images\n- **Pull:** `pelagos image pull alpine` — anonymous pulls from any OCI registry\n- **Run:** `pelagos run alpine /bin/sh` — multi-layer overlay, image config applied\n- **Build:** `pelagos build -t myapp:latest` — Remfile (Dockerfile-compatible syntax)\n  with multi-stage builds, ARG, ADD (URLs + archives), `.remignore`, build cache\n- **Manage:** `pelagos image ls` / `pelagos image rm`\n\n### WebAssembly / WASI\n- **Magic-byte dispatch:** `spawn()` reads the first 4 bytes; `\\0asm` triggers the\n  Wasm path automatically — the full Linux machinery (namespaces, overlayfs, seccomp,\n  pivot_root) is bypassed entirely\n- **OCI Wasm images:** pull, run, and build Wasm images from any OCI registry;\n  `pelagos image ls` shows a `TYPE` column (`linux` / `wasm`)\n- **WASI env + bind mounts:** `--env` passthrough and `--bind host:guest` directory\n  mapping with correct host→guest distinction\n- **Runtime dispatch:** wasmtime or wasmedge, auto-detected from PATH\n- **containerd shim:** `containerd-shim-pelagos-wasm-v1` implements ttrpc shim v2 —\n  schedule Wasm pods in Kubernetes via a `RuntimeClass` without a separate node agent\n- **`pelagos build` Wasm target:** `FROM scratch` + a `.wasm` output auto-detected\n  by magic bytes, stored with `application/wasm` OCI media type\n\n### Multi-Service Orchestration\n- **`pelagos compose up/down/ps/logs`:** dependency-ordered service lifecycle\n- **TCP readiness:** `:ready-port` polling before dependent services start\n- **Scoped resources:** networks, volumes, and container names prefixed per project\n- **Lifecycle hooks:** `on-ready` callbacks between startup tiers\n\n### Other\n- **Interactive containers:** PTY, SIGWINCH relay, terminal restore\n- **`pelagos exec`:** run a command inside a running container (namespace join + PTY)\n- **OCI Runtime Spec:** `create` / `start` / `state` / `kill` / `delete` lifecycle\n- **Rootless-first:** pull, build, run, overlay, and pasta networking without root — root is an opt-in escape hatch, not the default\n\n---\n\n## Installation\n\nDownload a pre-built static binary from the\n[Releases](https://github.com/pelagos-containers/pelagos/releases) page (x86_64 and\naarch64 Linux, statically linked musl), or build from source:\n\n```bash\n# Install to /usr/local/bin:\nscripts/install.sh\n\n# Or via cargo:\ncargo install --path .\n```\n\n---\n\n## Quick Start\n\nPelagos defaults to **rootless** — most operations work without `sudo`. Root is\nrequired only for bridge networking, NAT, port mapping, and OCI lifecycle commands\n(`create`/`start`/`kill`/`delete`).\n\nNetwork mode is auto-selected based on what you ask for:\n\n- **`-p` or `--nat` requested** → bridge + NAT (requires root; `-p` alone is enough, no `--network` flag needed)\n- **Neither** → pasta for full IPv4/IPv6 internet (`pasta` from [passt.top](https://passt.top)), or loopback with a warning if pasta is not installed\n\nUse `--network \u003cmode\u003e` to override explicitly.\n\nOn kernel 5.11+ Pelagos uses native overlayfs with `userxattr` (zero-copy,\nkernel-native). On older kernels it falls back to `fuse-overlayfs` automatically.\n\n### Rootless (no sudo)\n\n```bash\npelagos image pull alpine\npelagos run alpine /bin/echo hello\n\n# Interactive shell with internet (pasta auto-selected)\npelagos run -i alpine /bin/sh\n```\n\n### Root (port mapping, bridge networking)\n\n```bash\n# Internet access as root — pasta, same as rootless, no flags needed\nsudo pelagos run -i alpine /bin/sh\n\n# Port mapping — bridge + NAT are auto-selected when -p is given\nsudo pelagos run -d --name mybox -p 8080:80 alpine /bin/sh -c 'httpd -f'\n\n# Detached container without port mapping — also pasta\nsudo pelagos run -d --name mybox alpine \\\n  /bin/sh -c 'while true; do echo tick; sleep 1; done'\n\npelagos ps\npelagos logs -f mybox\nsudo pelagos stop mybox \u0026\u0026 pelagos rm mybox\n```\n\n### Multi-service stack\n\n```bash\n# A minimal stack — all compose files are Lisp programs (.reml)\nsudo -E pelagos compose up -f examples/compose/web-stack/compose.reml -p demo\n\n# With scripting: migrations, conditional startup, parallel execution\nsudo -E pelagos compose up -f examples/compose/imperative/compose.reml -p demo\n```\n\n---\n\n## Rust Library API\n\n```rust\nuse pelagos::container::{Command, Namespace};\n\nlet mut child = Command::new(\"/bin/sh\")\n    .args(\u0026[\"-c\", \"echo hello from container\"])\n    .with_chroot(\"/path/to/rootfs\")\n    .with_namespaces(Namespace::UTS | Namespace::MOUNT | Namespace::PID)\n    .with_proc_mount()\n    .with_seccomp_default()\n    .drop_all_capabilities()\n    .with_cgroup_memory(256 * 1024 * 1024)\n    .spawn()?;\n\nchild.wait()?;\n```\n\n```rust\n// Interactive shell\nlet session = Command::new(\"/bin/sh\")\n    .with_chroot(\"/path/to/rootfs\")\n    .with_namespaces(Namespace::UTS | Namespace::MOUNT)\n    .with_proc_mount()\n    .spawn_interactive()?;\n\nsession.run()?;  // relays stdin/stdout, forwards SIGWINCH, restores terminal\n```\n\nSee the [CLI-to-API translation table](docs/USER_GUIDE.md#cli-to-api-translation)\nin the user guide.\n\n---\n\n## Testing\n\n```bash\n# Unit tests (no root required):\ncargo test --lib\n\n# Integration tests (require root + alpine-rootfs):\nsudo -E cargo test --test integration_tests\n\n# E2E scripts (require root + built binary):\nsudo scripts/test-e2e.sh        # full CLI lifecycle\nsudo scripts/test-rootless.sh   # rootless mode (run without sudo)\nsudo scripts/test-build.sh      # pelagos build / Remfile parser\nsudo scripts/test-stress.sh     # concurrent containers, signal handling\n```\n\nSee [`docs/TESTING.md`](docs/TESTING.md) for the full guide: first-time setup,\nrunning subsets by category, environment reset, and troubleshooting. See\n[`docs/INTEGRATION_TESTS.md`](docs/INTEGRATION_TESTS.md) for per-test\ndocumentation.\n\n---\n\n## Architecture\n\n### Pre-exec hook order\n\n1. **Parent** — opens namespace files, compiles seccomp BPF, sets up bridge netns\n2. **Fork**\n3. **Child pre_exec** — unshare → UID/GID maps → setuid/setgid → chroot/pivot_root\n   → mounts → **capability drop** → rlimits → setns → seccomp (must be last)\n4. **exec** — replace child with target program\n\nCapability drop comes after all mount operations (masked paths, read-only rootfs)\nbecause those mounts require `CAP_SYS_ADMIN`. Seccomp is last because setup\nrequires syscalls it would otherwise block.\n\n---\n\n## Documentation\n\n| File | Contents |\n|------|----------|\n| [`docs/USER_GUIDE.md`](docs/USER_GUIDE.md) | CLI and API reference |\n| [`docs/REML_EXECUTOR_MODEL.md`](docs/REML_EXECUTOR_MODEL.md) | Lisp scripting: futures graph, `run`, `then`, parallel execution |\n| [`docs/TESTING.md`](docs/TESTING.md) | Testing guide: setup, categories, E2E scripts, troubleshooting |\n| [`docs/INTEGRATION_TESTS.md`](docs/INTEGRATION_TESTS.md) | Every integration test documented |\n| [`docs/DESIGN_PRINCIPLES.md`](docs/DESIGN_PRINCIPLES.md) | Non-negotiable design principles |\n| [`docs/ROADMAP.md`](docs/ROADMAP.md) | What's done and what's next |\n| [`docs/FEATURE_GAPS.md`](docs/FEATURE_GAPS.md) | Gap analysis vs. Docker Desktop / Finch |\n| [`docs/RUNTIME_COMPARISON.md`](docs/RUNTIME_COMPARISON.md) | Full feature matrix vs runc/Docker |\n| [`docs/SECCOMP_DEEP_DIVE.md`](docs/SECCOMP_DEEP_DIVE.md) | Seccomp-BPF implementation details |\n| [`docs/PTY_DEEP_DIVE.md`](docs/PTY_DEEP_DIVE.md) | PTY/interactive session design |\n| [`docs/CGROUPS.md`](docs/CGROUPS.md) | Cgroups v1 vs v2 analysis |\n| [`docs/BUILD_ROOTFS.md`](docs/BUILD_ROOTFS.md) | How to build the Alpine rootfs |\n| [`CHANGELOG.md`](CHANGELOG.md) | Version history and release notes |\n\n---\n\n## Requirements\n\n- Linux kernel 5.11+ recommended (rootless overlay with `userxattr`)\n- Kernel 5.0+ works with root, or rootless with `fuse-overlayfs` installed\n- `pasta` ([passt](https://passt.top)) for rootless networking\n- `nft` (nftables) for NAT and port mapping (root only)\n- `ip` (iproute2) for bridge networking (root only)\n- `fuse-overlayfs` for rootless overlay on kernels \u003c 5.11\n\n---\n\n## License\n\nSee LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpelagos-containers%2Fpelagos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpelagos-containers%2Fpelagos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpelagos-containers%2Fpelagos/lists"}