{"id":19635897,"url":"https://github.com/tozd/dinit","last_synced_at":"2025-08-16T16:40:24.827Z","repository":{"id":173301062,"uuid":"650533918","full_name":"tozd/dinit","owner":"tozd","description":"Specialized init for Docker containers. Read-only mirror of https://gitlab.com/tozd/dinit","archived":false,"fork":false,"pushed_at":"2023-11-21T08:39:17.000Z","size":199,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-04-17T20:13:41.890Z","etag":null,"topics":["docker","go","init"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":false,"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/tozd.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null},"funding":{"github":["plast8","mitar"]}},"created_at":"2023-06-07T09:16:15.000Z","updated_at":"2024-04-02T16:44:54.000Z","dependencies_parsed_at":"2023-10-31T22:00:22.907Z","dependency_job_id":null,"html_url":"https://github.com/tozd/dinit","commit_stats":null,"previous_names":["tozd/dinit"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tozd%2Fdinit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tozd%2Fdinit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tozd%2Fdinit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tozd%2Fdinit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tozd","download_url":"https://codeload.github.com/tozd/dinit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224102325,"owners_count":17256140,"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":["docker","go","init"],"created_at":"2024-11-11T12:27:26.749Z","updated_at":"2024-11-11T12:27:27.867Z","avatar_url":"https://github.com/tozd.png","language":"Go","readme":"# Specialized init for Docker containers\n\n[![Go Report Card](https://goreportcard.com/badge/gitlab.com/tozd/dinit)](https://goreportcard.com/report/gitlab.com/tozd/dinit)\n[![pipeline status](https://gitlab.com/tozd/dinit/badges/main/pipeline.svg?ignore_skipped=true)](https://gitlab.com/tozd/dinit/-/pipelines)\n[![coverage report](https://gitlab.com/tozd/dinit/badges/main/coverage.svg)](https://gitlab.com/tozd/dinit/-/graphs/main/charts)\n\nDocker containers should generally contain one service per container. But what happens when this service\nconsist of multiple different programs? Or when this service spawns sub-processes? Then a less-known fact about\nDocker containers comes into the effect: they all have the init process (PID 1)\nwhich has to reap zombie processes and handle signals from the container's supervisor. Using a program\nas the init process which does not expect to handle subprocesses and signals (e.g., distroless builds or naive\napplication bundles) properly can [lead to resource exhaustion or data loss](#what-was-the-motivation-to-start-this-project).\nDocker containers are similar but not exactly the same as a full Linux system so traditional init systems\nare not the best fit for Docker containers.\n\ndinit is an opinionated init for Docker containers which has been specially designed for operation\ninside Docker containers and does things slightly differently than traditional init systems but it\nmakes much more sense inside Docker containers.\n\nFeatures:\n\n- Multi-process:\n  - It supports running multiple different programs inside a Docker container, compatible with\n    [runit init system](http://smarden.org/runit/). If any program finishes, dinit terminates\n    the whole container so that container's supervisor can decide whether to restart the whole\n    container or do something else, e.g., a backoff or to even log that container has terminated.\n    Traditional init systems restart programs themselves, but that then hides any issues from the\n    container's supervisor. Moreover, traditional init systems generally do not do any backoff\n    between restarts.\n- Signal handling:\n  - On TERM signal it gracefully terminates all programs. It just sends them TERM signal as well\n    (by default) and then waits for them to terminate. It does not send them KILL signal because\n    the container's supervisor does that anyway if the whole container takes too long to terminate.\n- Managing processes:\n  - It reaps [zombie processes](https://en.wikipedia.org/wiki/Zombie_process) so that they do not\n    accumulate inside a Docker container.\n  - It extends reaping of zombie processes to handling any running process which gets reparented to dinit\n    (when the parent of such process exits before its child, e.g., when process is\n    [daemonized](\u003chttps://en.wikipedia.org/wiki/Daemon_(computing)\u003e)).\n    By default it terminates such processes (any daemonization is seen as configuration error)\n    but it also supports adopting such processes. When dinit adopts a reparented process it\n    redirects stdout and stderr of the process to dinit itself.\n  - Instead of default TERM signal one can provide a `terminate` file to be run to terminate\n    the main program (e.g., which can call `nginx -s quit`).\n- Managing processes' stdout and stderr:\n  - It line-wise multiplexes stdout and stderr from programs into its own stdout and stderr\n    so that all logs are available through `docker logs` or similar log collecting mechanism.\n  - To every logged line it adds the program's name and timestamp metadata. When configured that\n    stdout contains JSON per line (the default), it adds metadata as JSON fields, otherwise it\n    prepends metadata to every line. It prepends metadata to stderr as well.\n  - It uses stderr for its own errors. The idea here is that stdout should be used for expected\n    logging from programs while anything written to stderr by dinit or any program is exceptional\n    and that it cannot be assured to be JSON (e.g., Go runtime panic).\n  - Supports a logging program which then receives stdout from the main program. You can use it\n    to redirect stdout to a file or elsewhere, or to convert non-JSON stdout to JSON\n    (e.g., using [regex2json](https://gitlab.com/tozd/regex2json) tool). Stdout output\n    from the logging program is then used by dinit as stdout of the main program.\n- Configuration of dinit itself is done through environment variables.\n\n## Installation\n\ndinit requires Docker 19.03 or newer and Linux kernel versions 4.8 or newer.\n\n[Releases page](https://gitlab.com/tozd/dinit/-/releases)\ncontains a list of stable versions. Each includes statically compiled binaries.\nYou should just download the latest one inside your Dockerfile.\n\nYou can also use [tozd/dinit](https://gitlab.com/tozd/docker/dinit) Docker image as a base\nimage for your Docker images.\n\ndinit is implemented in Go. You can also use `go install` to install the latest stable (released) version:\n\n```sh\ngo install gitlab.com/tozd/dinit/cmd/dinit@latest\n```\n\nTo install the latest development version (`main` branch):\n\n```sh\ngo install gitlab.com/tozd/dinit/cmd/dinit@main\n```\n\n## Usage\n\nYou should configure\ndinit as the [entrypoint](https://docs.docker.com/engine/reference/builder/#entrypoint) in your Docker image.\nWhen Docker image runs, dinit will then look into `/etc/service` directory (by default, see `DINIT_DIR`)\nfor configuration of programs to run. The structure of `/etc/service` directory is\n[compatible with runit](http://smarden.org/runit/runsv.8.html) and consists of the following executable\nfiles for each program to run:\n\n- `/etc/service/\u003cname\u003e/run`: The main executable file which is run to start a program. Generally it is a\n  shell script which prepares program for execution and then [exec](\u003chttps://en.wikipedia.org/wiki/Exec_(system_call)\u003e)\n  into the executable of the program you want to run.\n  If `run` file finishes with code 115 it signals that the program is disabling itself and that it does not\n  have to run and the rest of the whole container is then not terminated as it would otherwise be when any\n  of its programs finishes.\n- `/etc/service/\u003cname\u003e/terminate`: When present, dinit does not send TERM signal to the process when it wants\n  to terminate it, but runs this executable file. When this file is executed, it receives the PID of the\n  corresponding terminating process through `DINIT_PID` environment variable.\n  Remember, you do not have to KILL the process, just initiate termination.\n  Container's supervisor will KILL any remaining processes anyway.\n- `/etc/service/\u003cname\u003e/log/run`: Optional executable file for a logging program. Stdout of the main program\n  (i.e., from `/etc/service/\u003cname\u003e/run`) is piped to stdin of this program which can then process it.\n  It can be use to redirect stdout to a file or elsewhere, or to convert non-JSON stdout to JSON\n  (e.g., using [regex2json](https://gitlab.com/tozd/regex2json) tool). Stdout output\n  from the logging program is then used by dinit as stdout of the main program.\n  Stderr outputs of the main and logging programs are used by dinit normally as well.\n\ndinit expects programs to not daemonize but to stay running with dinit as their parent process.\nIf any program does daemonize, the default `terminate` reparenting policy will simply terminate them.\n(`adopt` reparenting policy will adopt such processes, but that should be more of an exception than a rule.)\n\n### Configuration\n\nConfiguration of dinit itself is done through environment variables:\n\n- `DINIT_JSON_STDOUT`: By default dinit expects stdout lines to be JSON objects. It does a basic check\n  to verify this is so and if not it complains to its stderr. Set this variable to `0` to disable JSON\n  processing. Setting it to `0` also makes dinit prepend program's name and timestamp metadata to\n  the line instead of adding metadata as JSON fields.\n- `DINIT_LOG_LEVEL`: The level at which dinit logs. Default is `warn`. Possible levels are `none`,\n  `error`, `warn`, `info`, and `debug`.\n- `DINIT_REPARENTING_POLICY`: Default is `terminate`. Possible policies are `adopt`, `terminate`, and\n  `ignore`. `terminate` policy terminates any process which gets reparented to dinit.\n  `adopt` policy waits for the process to terminate (and then terminates the whole container). When adopting\n  a process dinit also redirects stdout and stderr of the process to dinit itself.\n- `DINIT_KILL_TIMEOUT`: How long (in seconds) does `terminate` policy waits after sending the TERM signal\n  to send the KILL signal to a reparented process? Default is 30 seconds.\n- `DINIT_DIR`: In which directory to look for programs to run. Default is `/etc/service`.\n\n## What was the motivation to start this project?\n\nIn [our Docker images](https://gitlab.com/tozd/docker) we used\n[runit init system](https://gitlab.com/tozd/docker/runit) but we discovered that\nimages [are not gracefully shut down](https://gitlab.com/tozd/docker/runit/-/issues/1). For example,\ndatabases were often not cleanly shut down. This happens because after runit receives the TERM signal\nand passes it on to running processes it immediately terminates itself causing Docker to believe that\nthe container has finished, after which Docker KILLs any remaining processes, including the database\nwhich has not yet cleanly shut down.\n\nOnce we started thinking about replacing runit we could not find any [existing project](#related-projects)\nwhich would provide all of the features we wanted, so a new project was started.\n\n## Why is JSON used just for stdout and not also for stderr?\n\nIt is hard to generate proper JSON once things start falling apart (e.g.,\n[Go runtime panic](https://github.com/golang/go/issues/40238)). The idea is that under default logging level,\nstdout should be used for expected logging from programs while anything written to stderr by dinit or any program\nis exceptional and means a human intervention is needed. You should setup programs run by dinit this way as well\n(defining a logging program can help you with that).\n\n## runit supports dependencies between programs, why not dinit?\n\nrunit compatibility is in how programs to run are specified.\nBut there are many aspects of runit which are not supported by dinit (e.g., dinit does not expose status\ninformation of programs through files and does not create control named pipes) which also prevents\n[waiting for another program to start](http://smarden.org/runit/faq.html#depends). There are two reasons for\nthis. First, creating files inside `DINIT_DIR` directory (like runit does) requires `DINIT_DIR` to be writable,\nbut writing outside of volumes in Docker containers is discouraged. Second, waiting for another program to start does\nnot necessarily mean that another program is also ready. This means that often it is better to have a\nprogram-specific way to test if another program is ready which can be done inside the `run` file.\n\n## Are there any examples of real service files?\n\nMany [tozd Docker images](https://gitlab.com/tozd/docker) use dinit any you can check files there, e.g.,\n[nginx](https://gitlab.com/tozd/docker/nginx/-/blob/master/etc/service/nginx/run) and its\n[terminate](https://gitlab.com/tozd/docker/nginx/-/blob/master/etc/service/nginx/terminate),\n[mongodb](https://gitlab.com/tozd/docker/mongodb/-/blob/master/etc/service/mongod/run) and its\n[log](https://gitlab.com/tozd/docker/mongodb/-/blob/master/log/run) (or an\n[older one](https://gitlab.com/tozd/docker/mongodb/-/blob/master/log-3.0/run) which converts logs to JSON).\n\n## Related projects\n\n- [runit](http://smarden.org/runit/index.html) – Awesome init system which looks like it is suitable for use inside\n  Docker containers for its simplicity and small size, but it does not really work well.\n  [Discourse has this script](https://github.com/discourse/discourse_docker/blob/master/image/base/boot)\n  and [baseimage-docker has another one](https://github.com/phusion/baseimage-docker/blob/master/image/bin/my_init)\n  to address some issues.\n- [runsvinit](https://github.com/peterbourgon/runsvinit) – Another solution for issues with running runit inside\n  Docker containers. It suggests that one should run both `runit` and `runsvdir` and not just `runsvdir` inside\n  Docker containers and suggests to write your own `/etc/service/ctrlaltdel` to cleanup processes. dinit just does\n  the right thing and does not require you to write custom cleanup scripts.\n- [github.com/ramr/go-reaper](https://github.com/ramr/go-reaper) – Recognizes the same issue of zombie processes in Docker\n  containers when Go programs are used as the init process (PID 1) inside Docker containers and provides a library\n  for Go programs to reap them. dinit supports also non-Go programs.\n- [dumb-init](https://github.com/Yelp/dumb-init) – Init to run a program which is not expecting to be the init process.\n  Supports running only one such program per container.\n- [tini](https://github.com/krallin/tini) – Another init to run a program which is not expecting to be the init process.\n  Now bundled with Docker. Also limited to only one such program per container.\n- [s6-overlay](https://github.com/just-containers/s6-overlay) – Provides utilities for [s6](https://skarnet.org/software/s6/overview.html),\n  another popular init system, for easier use inside Docker containers. It shares many features and\n  [design goals](https://github.com/just-containers/s6-overlay#the-docker-way) with dinit and more and is very\n  configurable. dinit is compatible with runit. dinit is simpler, opinionated, and attempts to be less configurable\n  and simply do the right thing.\n\n## GitHub mirror\n\nThere is also a [read-only GitHub mirror available](https://github.com/tozd/dinit),\nif you need to fork the project there.\n","funding_links":["https://github.com/sponsors/plast8","https://github.com/sponsors/mitar"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftozd%2Fdinit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftozd%2Fdinit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftozd%2Fdinit/lists"}