{"id":15441667,"url":"https://github.com/mudler/poco","last_synced_at":"2025-04-16T05:03:03.942Z","repository":{"id":40997460,"uuid":"437048819","full_name":"mudler/poco","owner":"mudler","description":":ship: poCo - portable Containers. Create statically linked, portable binaries from container images (daemonless)","archived":false,"fork":false,"pushed_at":"2022-04-16T20:46:09.000Z","size":67,"stargazers_count":92,"open_issues_count":4,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T05:03:50.561Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","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/mudler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-10T16:46:37.000Z","updated_at":"2025-02-22T02:21:53.000Z","dependencies_parsed_at":"2022-08-27T21:52:19.539Z","dependency_job_id":null,"html_url":"https://github.com/mudler/poco","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mudler%2Fpoco","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mudler%2Fpoco/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mudler%2Fpoco/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mudler%2Fpoco/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mudler","download_url":"https://codeload.github.com/mudler/poco/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249200053,"owners_count":21228995,"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":"2024-10-01T19:21:59.456Z","updated_at":"2025-04-16T05:03:03.924Z","avatar_url":"https://github.com/mudler.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003cbr\u003e\n\t\u003cimg src=\"https://user-images.githubusercontent.com/2420543/145685818-e4b25b4c-dd7c-46af-b9ca-66d27d4f17e0.png\" width=128\n         alt=\"logo\"\u003e\u003cbr\u003e\n    poCo \n\u003cbr\u003e\n\u003c/h1\u003e\n\n\u003ch3 align=\"center\"\u003e    Containers -\u003e Binaries\u003cbr\u003eCreate statically linked, portable binaries from container images \u003c/h3\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://opensource.org/licenses/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/licence-GPL3-brightgreen\"\n         alt=\"license\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/mudler/poco/issues\"\u003e\u003cimg src=\"https://img.shields.io/github/issues/mudler/poco\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/made%20with-Go-blue\"\u003e\n  \u003ca href=\"https://goreportcard.com/report/github.com/mudler/poco\" target=_blank\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/mudler/poco\" alt=\"go report card\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\t \u003cbr\u003e\n    A simple, static golang bundler! \n\n\u003c/p\u003e\n\n\npoCo (_portable_-_Containers_) packs and converts container images into single, portable, statically linked binaries leveraging golang native `embed`.\n\n## :question: How it works\n\n`poCo` is extremely simple in the design.\n\n`poCo` generates and builds golang code which embeds the container content compressed as an asset. It does bundle the assets by using the native golang `embed` primitives. The resulting binary on the first run will extract the content over the application `store` and will execute the entrypoint in a pivotroot environment, without requiring special permissions.\n\n## :computer: Install\n\nDownload poCO from the [releases](https://github.com/mudler/poco/releases) and install it in your `PATH`. poCO releases are statically built, so no dependencies (besides `golang` to create `bundles`, are required)\n\n## :running: Run \n\npoCO is a no-frills binary bundler, we will see an example of how to bundle a container image into a binary.\n\nRequires:\n\n- `poco` installed\n- `sudo`\n- golang `\u003e1.17` installed in the system where are you building\n\npoCo bundles container images available remotely or locally (by specifying `--local` to the `bundle` subcommand).\n\nFor instance to pack the `alpine` image into a `sample` binary is as simple as:\n\n```bash\nCGO_ENABLED=0 ./poco bundle --image alpine --output sample\n```\n\n`CGO_ENABLED=0` will instruct the golang compiler behind the scenes to create a statically linked executable.\n\nWe can specify optionally a default entrypoint for the resulting binary with `--entrypoint`, which is by default set to `/bin/sh`.\n\nYou can run the `--help` subcommand on `sample` to inspect its output, and you will see there are available few options:\n\n```\n❯ ./sample --help     \nNAME:\n   sample - sample\n\nUSAGE:\n    [global options] command [command options] [arguments...]\n\nVERSION:\n   0.1\n\nDESCRIPTION:\n   sample\n\nAUTHOR:\n   sample\n\nCOMMANDS:\n   exec       \n   uninstall  \n   help, h    Shows a list of commands or help for one command\n\nGLOBAL OPTIONS:\n   --store value       Default application store. Empty for TMPDIR\n   --entrypoint value  Default application entrypoint (default: \"/bin/sh\")\n   --add-mounts value  Additional mountpoints\n   --mounts value      Default app mountpoints (default: \"/sys\", \"/tmp\", \"/run\")\n   --help, -h          show help\n   --version, -v       print the version\n```\n\nThe binary has some defaults that can be override during build time with `bundle`, to run the application (in our case, `sh` from alpine), just run:\n\n```\n./sample # spawns a new /bin/sh shell\n```\n\nYou can also pass all the args to the entrypoint of the binary (`/bin/sh`), by specifying `-`:\n\n```\n./sample - -c \"echo foo\"\n```\n\nSee the `example/` folder for a more complete example.\n\nSupports: `CGO_ENABLED`, `GOOS`, `GOARCH`, etc.\n\nIt can target all architectures supported by golang. \n\n### Github Action\n\nTo run it with Github actions, see [https://github.com/mudler/poco-github-action](https://github.com/mudler/poco-github-action)\n\n### `bundle`\n\n`bundle` creates a binary from a container image, it takes several options listed here:\n\n| Flag              | Description                                                                                                                                                                                            |\n|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| --entrypoint      | Default binary entrypoint. This is the first binary from the container image which will be executed. It defaults to `/bin/sh`                                                                          |\n| --output          | Default binary output location                                                                                                                                                                         |\n| --compression     | Compression format used to pack the container image into the bundle. Supported formats: (bz2, zst, gz, xz, lz4, br, sz)                                                                                |\n| --app-description | This is the description of the app that will be displayed in the resulting binary `--help`                                                                                                             |\n| --app-author      | This is the author of the app that will be displayed in the resulting binary  `--help`                                                                                                                 |\n| --app-name        | This is the name of the app that will be displayed in the resulting binary  `--help`                                                                                                                   |\n| --app-version     | This is the version of the app that will be displayed in the resulting binary  `--help`. The version will be used between different binary bundles to handle upgrades.                                 |\n| --local           | Tells poco to get the container image from the local Docker daemon instead of fetching it remotely. By default poco doesn't require a Docker daemon running locally                                    |\n| --app-mounts      | A list of default mount binding for the app. The application runs in a chroot-alike environment, without access to the files of the system unless explictly mounted. Multiple mounts can be specified. |\n| --app-store       | A default store for your app. This is where the bundle gets extracted before being executed, and where the real app data lives afterward on subsequent calls.                                          |\n| --image           | The container image to bundle.                                                                                                                                                                         |\n| --command-prefix  | Command prefix for auto-generated code. Usually you don't need to change that unless you are running the builds as root                                                                                |\n| --directory          | A directory to bundle (in place of the container image)                                                                                                                                                                         |\n\n#### Mounts\n\nA `poCo` bundle runs in a sandboxed environment. To expose directories or files, the resulting binary in runtime tales the `--mounts` or `--add-mounts` option (also multiple times) to specify a list of directories or files to expose from the host environment.\n\nWhile creating the binary, it is also possible to specify a default set, so the binary runs with the directory shared from the host already, this is possible by passing `--app-mounts`.\n\nFor instance, consider:\n\n```bash\nCGO_ENABLED=0 ./poco bundle --image alpine --output sample --app-mounts /tmp --app-mounts 'ro:/home/.bar:/home/.bar'\n```\n\nwill create a `sample` binary with `alpine` which `/tmp` will be mapped `rw` and `/home/.bar` `ro` from the host.\n\n#### Default store\n\nEvery application has a default store. By default, each application will unpack its content to a temporary directory. To change this behavior and persist data in the system which is running the app, specify a default location with `--app-store`.\n\nFor instance, the following will use the `~/.poco/alpine` folder to unpack the bundle content on the first run:\n```bash\nCGO_ENABLED=0 ./poco bundle --image alpine --output sample --app-store '$HOME/.poco/alpine'\n```\n\nEvery application can indeed be uninstalled, which just deletes the default `app-store`:\n\n```bash\n./sample uninstall\n```\n\n#### Metadata\n\nEvery generated bundle will have a default --help which is being displayed. It is possible to set metadata such as `description`, `name`, `author`, `copyright` that will be automatically available in the resulting binary `--help`. \n\nThe version is more relevant if a default `--app-store` is being specified. The `app-version` is used during the first run to determine if the installed bundle should be replaced or not.\n\n### `render`\n\n`render` allows to render the generated golang code into a specified directory. This is might be helpful if you want to change the generated binary before build.\n\n```\n$ mkdir alpine\n$ ./poco render --image alpine alpine\n$ ls alpine/\ngo.mod main.go\n```\n\n### `pack`\n\n`pack` is an internal utility to pack directories as container images that can be `docker` loaded afterwards:\n\n```\n$ mkdir foo\n$ touch foo/bar\n$ poco pack myimage:tag foo --destination output.tar\n$ docker load -i output.tar\n$ docker push myimage:tag\n```\n\n### `extract`\n\n`extract` is an internal utility to scan a binary and all its dynamic linked libraries. It will copy the binary and the libraries needed by it into the specified folder, respecting the path hierarchy.\n\n```\n$ poco extract /my/dynamic/bin /output\n$ poco pack myimage:tag /output --destination image.tar\n$ docker load -i image.tar\n...\n```\n\n### `pack-assets`\n\n`pack-assets` is an internal utility to pack assets for the bundle.\n\n```\n$ mkdir foo\n$ touch foo/bar\n$ poco pack-assets -C foo .\n$ ls\nassets.tar.xz\n\n```\n\n### `unpack`\n\n`unpack` is an internal utility to unpack a container image into a directory\n\n```\n$ mkdir alpine\n$ poco unpack alpine alpine\n$ ls alpine\nbin etc usr ...\n$ poco bundle --directory alpine ...\n```\n\n## :notebook: Troubleshooting\n\nWhen troubleshooting issues with bundles created by `poco`, it might be helpful to open a shell within a bundle:\n\n```bash\n./\u003cbundle\u003e --entrypoint /bin/sh\n```\n\nThe `--entrypoint` command is available in all binaries generated by `poco` and as such you can override the default entrypoint anytime.\n\n_NOTE_ The `--mounts` command is also available in all binaries generated by `poco` and will **OVERRIDE** the default mount points specified during bundle time. To have additional mounts besides default, use `--add-mounts` instead.\n\n## :warning: Notes\n\n- During build sudo is required in order to preserve container permissions. \n- By default bundles do have network access, but as Docker images mount during build /etc/resolv.conf, the file is empty in docker images, which results in no ability to resolv network address. In order to let the bundle read the `/etc/resolv.conf` from the host, use `--app-mounts /etc/resolv.conf`.\n\n## :mag: Examples\n\nSee the `examples` folder or [linuxbundles](https://mudler.github.io/linuxbundles/) for a collection of popular distributions packaged or either [caramel](https://mocaccinoos.github.io/caramel/) for a more complete example involving popular apps.\n\nThe pipeline builds the `firefox` image which can be downloaded and to run locally as a standard binary:\n\n```\n./firefox\n```\nTo uninstall:\n\n```\n./firefox uninstall\n```\n\n## :notebook: TODO\n\n- [ ] Multi-platform support (Windows, MacOS at least..)\n\n## :question: Why?\n\n¯\\_(ツ)_/¯\n\nSomeone told me this wasn't possible, so here we are.\n\nI know there is flatpak and also tons of AppImages out there and this is just yet another bundler for most of you, so to make it clear: the scope of this project is not even comparable to them. \n\nWhile I was sketching this up I realized I wanted something VERY simple that doesn't gets in the way and opinionated enough that can leverage already existing container image - without the need of additional docs or specific procedures for users. \nThis bundler might fit just simple and specific purposes - and most likely - people like me that doesn't have high end goals and rely on golang daily. \n\nSo focus of this project is - to prove a point of course - and on semplicity and portability rather than, for example, security.\n\nAnd besides, let's be frank. Building bundles with go+docker is really easy to go with as a stack.\n\n## :notebook: Credits\n\nDocker authors for the pivotroot code part, was very helpful read to get that right.\n\n# 🐜 Contribution\n\nYou can improve this project by contributing in following ways:\n\n- report bugs\n- fix issues\n- request features\n- asking questions (just open an issue)\n\nand any other way if not mentioned here.\n\n## :notebook: Author\n\npoCo is released under GPL-3, Copyright Ettore Di Giacinto \u003cmudler@mocaccino.org\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmudler%2Fpoco","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmudler%2Fpoco","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmudler%2Fpoco/lists"}