{"id":18603621,"url":"https://github.com/cogini/mix_systemd","last_synced_at":"2025-10-13T16:32:23.711Z","repository":{"id":62429888,"uuid":"145314144","full_name":"cogini/mix_systemd","owner":"cogini","description":"Library of mix tasks to generate a systemd unit file for an Elixir project","archived":false,"fork":false,"pushed_at":"2024-07-08T00:15:51.000Z","size":199,"stargazers_count":67,"open_issues_count":0,"forks_count":8,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-09-22T06:38:05.654Z","etag":null,"topics":["elixir","elixir-library","mix","mix-tasks","systemd-unit"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/cogini.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-08-19T15:18:29.000Z","updated_at":"2025-08-27T20:56:26.000Z","dependencies_parsed_at":"2024-05-30T00:43:05.169Z","dependency_job_id":"e30ef38a-e996-4772-bd9e-7f036b869a0d","html_url":"https://github.com/cogini/mix_systemd","commit_stats":{"total_commits":152,"total_committers":4,"mean_commits":38.0,"dds":"0.046052631578947345","last_synced_commit":"a7c2b4d16e9bfdb11bbf5b24d47cb829352d6ce8"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cogini/mix_systemd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cogini%2Fmix_systemd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cogini%2Fmix_systemd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cogini%2Fmix_systemd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cogini%2Fmix_systemd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cogini","download_url":"https://codeload.github.com/cogini/mix_systemd/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cogini%2Fmix_systemd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":277081049,"owners_count":25757340,"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","status":"online","status_checked_at":"2025-09-26T02:00:09.010Z","response_time":78,"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":["elixir","elixir-library","mix","mix-tasks","systemd-unit"],"created_at":"2024-11-07T02:14:54.845Z","updated_at":"2025-09-26T12:59:59.095Z","avatar_url":"https://github.com/cogini.png","language":"Elixir","readme":"![test workflow](https://github.com/cogini/mix_systemd/actions/workflows/test.yml/badge.svg)\n[![Module Version](https://img.shields.io/hexpm/v/mix_systemd.svg)](https://hex.pm/packages/mix_systemd)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/mix_systemd/)\n[![Total Download](https://img.shields.io/hexpm/dt/mix_systemd.svg)](https://hex.pm/packages/mix_systemd)\n[![License](https://img.shields.io/hexpm/l/mix_systemd.svg)](https://github.com/cogini/mix_systemd/blob/master/LICENSE.md)\n[![Last Updated](https://img.shields.io/github/last-commit/cogini/mix_systemd.svg)](https://github.com/cogini/mix_systemd/commits/master)\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)\n\n# mix_systemd\n\nThis library generates a\n[systemd](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)\nunit file to manage an Elixir release. It supports releases generated by Elixir 1.9+\n[mix release](https://hexdocs.pm/mix/Mix.Tasks.Release.html) or\n[Distillery](https://hexdocs.pm/distillery/home.html).\n\nAt its heart, it's a mix task which reads information about the project from\n`mix.exs` and `config/config.exs` then generates systemd unit files using Eex\ntemplates. The goal is that the project defaults will generate a good systemd\nunit file, and standard options support more specialized use cases.\n\nIt uses standard systemd functions and conventions to make your app a more\n\"native\" OS citizen and takes advantage of systemd features to improve\nsecurity and reliability. While it can be used standalone, more advanced use\ncases use scripts from e.g., [mix_deploy](https://github.com/cogini/mix_deploy).\n\nThis [complete example app](https://github.com/cogini/mix-deploy-example) puts the\npieces together.\n\n## Installation\n\nAdd `mix_systemd` to the list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:mix_systemd, \"~\u003e 0.7\"},\n  ]\nend\n```\n\n## Configuration\n\nThe library tries to choose reasonable defaults, so you may not need to\nconfigure anything. It reads the app name from `mix.exs` and calculates default\nvalues for its configuration parameters. For example, if your app is named\n`foo_bar`, it will create a service named `foo-bar`, deployed to\n`/srv/foo-bar`, running under the user `foo-bar`.\n\nYou can override these parameters using settings in `config/config.exs`, e.g.:\n\n```elixir\nconfig :mix_systemd,\n  app_user: \"app\",\n  app_group: \"app\",\n  base_dir: \"/opt\",\n  env_vars: [\n    \"PORT=8080\",\n  ]\n```\n\n## Configuration strategies\n\nThere are four different kinds of things that we may want to configure:\n\n1. Static information about application layout, e.g., file paths.\n   This is the same for all machines in an environment, e.g., staging or prod.\n\n2. Information specific to the environment, e.g., the hostname of the db\n   server.\n\n3. Secrets such as db passwords, API keys, or encryption keys.\n\n4. Dynamic information such as the IP address of the server or other\n   machines in the cluster.\n\nElixir has a couple of mechanisms for storing configuration. When you compile\nthe release, it converts Elixir-format config files like `config/config.exs`\ninto an initial application environment that is read by `Application.get_env/3`.\nThat's good for simple, relatively static apps. It's not ideal to store\npasswords in the release file, though.\n\nElixir 1.9+ releases support dynamic configuration at runtime. You can configure\nvia the Elixir file `config/runtime.exs` which is loaded when the VM boots or\nuse the shell script `rel/env.sh.eex` to set environment vars.\n\nWith these you can theoretically do anything. In practice, however, it can be\nmore convenient and secure to process the config outside of the app. That's\nwhere `mix_systemd` and `mix_deploy` come in.\n\n## Environment vars\n\nThe simplest thing is to set environment variables. Add individual vars to\n`env_vars`, and they will be set in the systemd unit file. Add files to\n`env_files` and systemd will load them before starting your app.\nYour application then calls `System.get_env/1` in `config/runtime.exs` or\napplication startup. Note that these environment vars are read at *runtime*,\nnot when building your app.\n\n```elixir\nenv_vars: [\n  # Set a variable, good for things that are not sensitive and don't change\n  \"PORT=8080\",\n],\ndirs: [\n  # create /etc/foo\n  :configuration,\n],\nenv_files: [\n  # Read environment vars from the file /etc/foo/environment\n  [\"-\", :configuration_dir, \"/environment\"],\n]\n```\n\n`/etc/foo/environment` looks like:\n\n    DATABASE_URL=\"ecto://foo_prod:Sekrit!@db.foo.local/foo_prod\"\n    SECRET_KEY_BASE=\"EOdJB1T39E5Cdeebyc8naNrOO4HBoyfdzkDy2I8Cxiq4mLvIQ/0tK12AK1ahrV4y\"\n    HOST=\"www.example.com\"\n    ASSETS_HOST=\"assets.example.com\"\n    RELEASE_COOKIE=\"LmCMGNz04yEJ4MQc6jt3cS7QjAppYOw_bQa7NE5hPZJGqL3Yry1jUg==\"\n\n`config/runtime.exs` does something like the following (the default files are good):\n\n```elixir\nconfig :foo, Foo.Repo,\n  url: System.get_env(\"DATABASE_URL\")\n\nconfig :foo, FooWeb.Endpoint,\n  http: [:inet6, port: System.get_env(\"PORT\") || 4000],\n  url: [host: System.get_env(\"HOST\"), port: 443],\n  static_url: [host: System.get_env(\"ASSETS_HOST\"), port: 443],\n  secret_key_base: System.get_env(\"SECRET_KEY_BASE\"),\n  cache_static_manifest: \"priv/static/cache_manifest.json\"\n```\n\n### Copying files\n\nThe question is how to get the environment files onto the server. For simple\nserver deployments, we can copy the config to the server when doing the initial\nsetup.\n\nIn cloud environments, we may run from a read-only image, e.g., an Amazon AMI,\nwhich gets configured at start up based on the environment by copying the\nconfig from an S3 bucket, e.g.:\n\n```shell\numask 077\naws s3 sync --exact-timestamps --no-progress \"s3://${CONFIG_BUCKET}/\" \"/etc/foo/\"\nchown -R $DEPLOY_USER:$APP_GROUP /etc/foo\nfind /etc/foo -type f -exec chmod 640 {} \\;\nfind /etc/foo -type d -exec chmod 750 {} \\;\n```\n\nThe following example runs the script `/srv/foo/bin/deploy-sync-config-s3` from\n`mix_deploy`.  It uses an environment file in `/srv/foo/etc/environment`\nto bootstrap the sync, e.g., setting the S3 bucket name. That file\nis placed there by CodeDeploy at deploy time.\n\n```elixir\nconfig :mix_systemd,\n  exec_start_pre: [\n    # Run before starting the app\n    # The `!` means the script is run as root, not as the app user\n    [\"!\", :deploy_dir, \"/bin/deploy-sync-config-s3\"]\n  ],\n  dirs: [\n    :configuration, # /etc/foo, app configuration, e.g. db passwords\n    :runtime,       # /run/foo, temp files which may be deleted between runs\n  ],\n  env_files: [\n    [\"-\", :deploy_dir, \"/etc/environment\"], # /srv/foo/etc/environment\n  ]\n  env_vars: [\n    # Tell release to use /run/foo for temp files\n    [\"RELEASE_TMP=\", :runtime_dir],\n  ]\n```\n\n### Config providers\n\nAt a certain point, making everything into an environment var becomes annoying.\nIt's verbose and vars are simple strings, so you have to encode values\nsafely and convert them back to lists, integers or atoms.\n\n[Config providers](https://hexdocs.pm/elixir/Config.Provider.html) let you load\nfiles in standard formats like [TOML](https://hexdocs.pm/toml_config/readme.html).\n\n```toml\n[foo.\"Foo.Repo\"]\nurl = \"ecto://foo_prod:Sekrit!@db.foo.local/foo_prod\"\npool_size = 15\n\n[foo.\"FooWeb.Endpoint\"]\nsecret_key_base = \"EOdJB1T39E5Cdeebyc8naNrOO4HBoyfdzkDy2I8Cxiq4mLvIQ/0tK12AK1ahrV4y\"\n```\n\nThe app reads these config files on startup and merges them into the app\nconfig.\n\n```elixir\ndefp releases do\n   [\n     prod: [\n       include_executables_for: [:unix],\n       config_providers: [\n         {TomlConfigProvider, path: \"/etc/foo/config.toml\"}\n       ],\n       steps: [:assemble, :tar]\n     ]\n   ]\n end\n```\n\nThe startup scripts read the initial application environment compiled into the\nrelease, parse the config file, merge the values, write it to a temp file, then\nstart the VM. Because of that, they need a writable directory. That is\nconfigured using the `RELEASE_TMP` environment var, normally set to the app's\n`runtime_dir`.\n\n```elixir\ndirs: [\n  :configuration,\n  :runtime,\n],\nenv_vars: [\n  [\"RELEASE_TMP=\", :runtime_dir],\n],\n```\n\n### Config servers and vaults\n\nYou can also store config params in an external configuration system and\nread them at runtime. An example is [AWS Systems Manager Parameter\nStore](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html).\n\nSet a parameter using the AWS CLI:\n\n```shell\naws ssm put-parameter --name '/foo/prod/db/password' --type ‘SecureString’ --value 'Sekrit!\"\n```\n\nWhile it's possible to read params in `config/runtime.exs`, it's tedious.\nBetter is to grab all of them at once and write them to a file, then read it in\nwith a Config Provider like [aws_ssm_provider](https://github.com/caredox/aws_ssm_provider).\n\n```shell\naws --region us-east-1 ssm get-parameters-by-path --path \"/foo/prod/\" --recursive --with-decryption --query \"Parameters[]\" \u003e /etc/foo/ssm.json\n```\n\n```elixir\ndefp releases do\n   [\n     prod: [\n       include_executables_for: [:unix],\n       config_providers: [\n         {AwsSsmProvider, path: \"/etc/foo/ssm.json\"}\n       ],\n       steps: [:assemble, :tar]\n     ]\n   ]\n end\n```\n\n### Dynamic config\n\nYou can write code to do things like query the system for the primary IP\naddress, but `cloud-init`\n[already does it](https://cloudinit.readthedocs.io/en/latest/topics/instancedata.html).\nYou just have to read the JSON file.\n\nThe most common use for this is setting up the VM node name. In `env.sh`:\n\n```shell\nCLOUD_NAME=$(jq -r '.v1.cloud_name' \u003c /run/cloud-init/instance-data.json)\nif [ \"$CLOUD_NAME\" = \"digitalocean\" ]; then\n    IP_ADDR=$(jq -r '.ds.meta_data.interfaces.public[0].anchor_ipv4.ip_address' \u003c /run/cloud-init/instance-data.json)\n    DEFAULT_IPV4=\"$IP_ADDR\"\nelif [ \"$CLOUD_NAME\" = \"aws\" ]; then\n    IP_ADDR=$(jq -r '.ds.meta_data.\"local-ipv4\"' \u003c /run/cloud-init/instance-data.json)\n    # IP_ADDR=$(jq -r '.ds.meta_data.\"public-ipv4\"' \u003c /run/cloud-init/instance-data.json)\n    AWS_REGION=$(jq -r '.v1.region' \u003c /run/cloud-init/instance-data.json)\nfi\nRELEASE_DISTRIBUTION=\"name\"\nRELEASE_NODE=\"${RELEASE_NAME}@${IP_ADDR}\"\n```\n\n### Security\n\nAn important security principle is\n\"[least privilege](https://www.cogini.com/blog/improving-app-security-with-the-principle-of-least-privilege/)\".\nIf an attacker manages to compromise the app, then they can do whatever it has\npermissions to do, not just what you expect. Because of that, I prefer that the\naccount that the app runs under cannot write files, and having a writable\nconfig file that is also executed is the worst case scenario.\n\n## Usage\n\nFirst, use the `systemd.init` task to template files from the library to the\n`rel/templates/systemd` directory in your project.\n\n```shell\nmix systemd.init\n```\n\nNext, generate output files in the build directory under\n`_build/#{mix_env}/systemd/lib/systemd/system`.\n\n```shell\nMIX_ENV=prod mix systemd.generate\n```\n\n## Configuration options\n\nThe following sections describe common configuration options.\nSee `lib/mix/tasks/systemd.ex` for the details of more obscure options.\n\nIf you need to make changes not supported by the config options,\nthen you can check the templates in `rel/templates/systemd`\ninto source control and make your own changes.  Contributions are welcome!\n\n### Basics\n\n`app_name`: Elixir application name, an atom, from the `app` or `app_name`\nfield in the `mix.exs` project. For umbrella apps, set `app_name`.\n\n`module_name`: Elixir camel case module name version of `app_name`, e.g.,\n`FooBar`.\n\n`release_name`: Name of release, default `app_name`.\n\n`ext_name`: External name, used for files and directories, default `app_name`\nwith underscores converted to \"-\", e.g., `foo-bar`.\n\n`service_name`: Name of the systemd service, default `ext_name`.\n\n`release_system`: `:mix | :distillery`, default `:mix`\n\nIdentifies the system used to generate the releases,\n[Mix](https://hexdocs.pm/mix/Mix.Tasks.Release.html) or\n[Distillery](https://hexdocs.pm/distillery/home.html).\n\n### Users\n\n`app_user`: OS user account that the app runs under, default `ext_name`.\n\n`app_group`: OS group account, default `ext_name`.\n\n### Directories\n\n`base_dir`: Base directory for app files on target, default `/srv`.\n\n`deploy_dir`: Directory for app files on target, default `#{base_dir}/#{ext_name}`.\n\nWe use the\n[standard app directories](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RuntimeDirectory=),\nfor modern Linux systems. App files are under `/srv`, configuration under\n`/etc`, transient files under `/run`, data under `/var/lib`.\n\nDirectories are named based on the app name, e.g. `/etc/#{ext_name}`.\nThe `dirs` variable specifies which directories the app uses.\nBy default, it doesn't set up anything. To enable them, configure the `dirs`\nparam, e.g.:\n\n```elixir\ndirs: [\n  # :runtime,       # App runtime files which may be deleted between runs, /run/#{ext_name}\n  # :configuration, # App configuration, e.g. db passwords, /etc/#{ext_name}\n  # :state,         # App data or state persisted between runs, /var/lib/#{ext_name}\n  # :cache,         # App cache files which can be deleted, /var/cache/#{ext_name}\n  # :logs,          # App external log files, not via journald, /var/log/#{ext_name}\n  # :tmp,           # App temp files, /var/tmp/#{ext_name}\n],\n```\n\nRecent versions of systemd (since 235) will create these directories at\nstart time based on the settings in the unit file. With earlier systemd\nversions, create them beforehand using installation scripts, e.g.,\n[mix_deploy](https://github.com/cogini/mix_deploy).\n\nFor security, we set permissions to 750, more restrictive than the systemd\ndefaults of 755. You can configure them with variables like\n`configuration_directory_mode`. See the defaults in\n`lib/mix/tasks/systemd.ex`.\n\n`systemd_version`: Sets the systemd version on the target system, default 235.\nThis determines which systemd features the library will enable. If you are\ntargeting an older OS release, you may need to change it. Here are the systemd\nversions in common OS releases:\n\n* CentOS 7: 219\n* Ubuntu 16.04: 229\n* Ubuntu 18.04: 237\n\n### Additional directories\n\nThe library uses a directory structure under `deploy_dir` which supports\nmultiple releases, similar to [Capistrano](https://capistranorb.com/documentation/getting-started/structure/).\n\n* `scripts_dir`: deployment scripts which, e.g., start and stop the unit, default `bin`.\n* `current_dir`: where the current Erlang release is unpacked or referenced by symlink, default `current`.\n* `releases_dir`: where versioned releases are unpacked, default `releases`.\n* `flags_dir`: dir for flag files to trigger restart, e.g., when `restart_method` is `:systemd_flag`, default `flags`.\n\nWhen using multiple releases and symlinks, the deployment process works as follows:\n\n1. Create a new directory for the release with a timestamp like\n   `/srv/foo/releases/20181114T072116`.\n\n2. Upload the new release tarball to the server and unpack it to the releases dir.\n\n3. Make a symlink from `/srv/#{ext_name}/current` to the new release dir.\n\n4. Restart the app.\n\nIf you are only keeping a single version, then deploy it to the directory\n`/srv/#{ext_name}/current`.\n\n## Variable expansion\n\nThe following variables support variable expansion:\n\n```elixir\nexpand_keys: [\n  :env_files,\n  :env_vars,\n  :runtime_environment_service_script,\n  :exec_start_pre,\n  :exec_start_wrap,\n  :read_write_paths,\n  :read_only_paths,\n  :inaccessible_paths,\n]\n```\n\nYou can specify values as a list of terms, and it will look up atoms as keys in\nthe config. This lets you reference, e.g., the deploy dir or configuration dir without\nhaving to specify the full path, e.g., `[\"!\", :deploy_dir, \"/bin/myscript\"]` gets\nconverted to `\"!/srv/foo/bin/myscript\"`.\n\n### Environment vars\n\nThe library sets env vars in the unit file:\n\n* `MIX_ENV`: `mix_env`, default `Mix.env()`\n* `LANG`: `env_lang`, default `en_US.utf8`\n\n* `RUNTIME_DIR`: `runtime_dir`, if `:runtime` in `dirs`\n* `CONFIGURATION_DIR`: `configuration_dir`, if `:configuration` in `dirs`\n* `LOGS_DIR`: `logs_dir`, if `:logs` in `dirs`\n* `CACHE_DIR`: `cache_dir`, if `:cache` in `dirs`\n* `STATE_DIR`: `state_dir`, if `:state` in `dirs`\n* `TMP_DIR`: `tmp_dir`, if `:tmp` in `dirs`\n\nYou can set additional vars using `env_vars`, e.g.:\n\n```elixir\nenv_vars: [\n  \"PORT=8080\",\n]\n```\nYou can also reference the value of other parameters by name, e.g.:\n\n```elixir\nenv_vars: [\n  [\"RELEASE_TMP=\", :runtime_dir],\n]\n```\n\nYou can read environment vars from files with `env_files`, e.g.:\n\n```elixir\nenv_files: [\n  [\"-\", :deploy_dir, \"/etc/environment\"],\n  [\"-\", :configuration_dir, \"environment\"],\n  [\"-\", :runtime_dir, \"environment\"],\n],\n```\n\nThe \"-\" at the beginning makes the file optional; the system will start without it.\nLater values override earlier values, so you can set defaults which get\noverridden in the local or runtime environment.\n\n\n### Runtime dirs\n\nThe release scripts may need to write temp files and log files, e.g., when\ngenerating the application config files. By default, they do this under\nthe release dir, e.g., `/srv/foo/current/tmp`.\n\nFor security, it's better to deploy the app using a different user account from\nthe one that the app runs under, with the source files read only. This makes\nit harder for an attacker to make changes to the source and then have the app\nrun them.\n\nIn that case, we need to set an environment var which tells the release\nstartup scripts where they can write files. For Mix releases, that is\n`RELEASE_TMP` and for Distillery it is `RELEASE_MUTABLE_DIR`, e.g.:\n\n```elixir\nenv_vars: [\n  {\"RELEASE_TMP=\", :runtime_dir},\n]\n```\n\nBy default systemd will delete the runtime directory when restarting the app.\nThis can be annoying when debugging startup issues. You can set\n`runtime_directory_preserve` to `restart` or `yes` (see\n[RuntimeDirectoryPreserve](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RuntimeDirectoryPreserve=)).\n\n### Starting and restarting\n\nThe following variables set systemd variables:\n\n`service_type`: `:simple | :exec | :notify | :forking`. systemd\n[Type](https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=), default `:simple`.\n\nModern applications don't fork, they run in the foreground and rely on the\nsupervisor to manage them as a daemon. This is done by setting `service_type`\nto `:simple` or `:exec`. Note that in `simple` mode, systemd doesn't actually\ncheck if the app started successfully, it just continues starting other units.\nIf something depends on your app being up, `:exec` may be better.\n\nSet `service_type` to `:forking`, and the library sets `pid_file` to\n`#{runtime_directory}/#{app_name}.pid` and sets the `PIDFILE` env var to tell\nthe boot scripts where it is.\n\nThe Erlang VM runs pretty well in foreground mode, but traditionally runs as\nas a standard Unix-style daemon, so forking might be better. Systemd\nexpects foregrounded apps to die when their pipe closes. See\nhttps://elixirforum.com/t/systemd-cant-shutdown-my-foreground-app-cleanly/14581/2\n\n`restart_method`: `:systemctl | :systemd_flag | :touch`, default `:systemctl`\n\nSet this to `:systemd_flag`, and the library will generate an additional\nunit file which watches for changes to a flag file and restarts the\nmain unit. This allows updates to be pushed to the target machine by an\nunprivileged user account which does not have permissions to restart\nprocesses. Touch the file `#{flags_dir}/restart.flag` and systemd will\nrestart the unit.\n\n`working_dir`: Current working dir for app. systemd\n[WorkingDirectory](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#WorkingDirectory=),\ndefault `current_dir`.\n\n`limit_nofile`: Limit on open files, systemd\n[LimitNOFILE](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LimitCPU=),\ndefault 65535.\n\n`umask`: Process umask, systemd\n[UMask](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=),\ndefault \"0027\". Note that this is octal, so it needs to be a string.\n\n`restart_sec`: Time in seconds to wait between restarts, systemd\n[RestartSec](https://www.freedesktop.org/software/systemd/man/systemd.service.html#RestartSec=),\ndefault 100ms.\n\n`syslog_identifier`: Logging name, systemd\n[SyslogIdentifier](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SyslogIdentifier=),\ndefault `service_name`\n\n\n## `ExecStartPre` scripts\n\nScripts specified in `exec_start_pre` (systemd\n[ExecStartPre](https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStartPre=)])\nrun before the main `ExecStart` script runs, e.g.:\n\n```elixir\nexec_start_pre: [\n  [\"!\", :deploy_dir, \"/bin/deploy-sync-config-s3\"]\n]\n```\n\nThis runs the `deploy-sync-config-s3` script from `mix_deploy`, which\ncopies config files from an S3 bucket into `/etc/foo`. By default,\nscripts run as the same user and group as the main script. Putting\n`!` in front makes the script run with [elevated\nprivileges](https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=),\nallowing it to write config to `/etc/foo` even if the main user account cannot for security reasons.\n\n#### ExecStart wrapper script\n\nInstead of running the main `ExecStart` script directly, you can run a shell script\nwhich sets up the environment, then runs the main script with `exec`.\nSet `exec_start_wrap` to the name of the script, e.g.\n`deploy-runtime-environment-wrap` from `mix_deploy`.\n\nIn Elixir 1.9+ releases you can use `env.sh`, but this runs earlier\nwith elevated permissions, so a wrapper script may still be useful.\n\n#### Runtime environment service\n\nYou can run your own separate service to configure the runtime environment\nbefore the app runs.  Set `runtime_environment_service_script` to a script such\nas `deploy-runtime-environment-file` from `mix_deploy`. This library will\ncreate a `#{service_name}-runtime-environment.service` unit and make it a\nsystemd runtime dependency of the app.\n\n### Runtime dependencies\n\nSystemd starts units in parallel when possible. To enforce ordering, set\n`unit_after_targets` to the names of systemd units that this unit depends on.\nFor example, if this unit should run after cloud-init to get [runtime network\ninformation](https://cloudinit.readthedocs.io/en/latest/topics/network-config.html#network-configuration-outputs),\nset:\n\n```elixir\nunit_after_targets: [\n  \"cloud-init.target\"\n]\n```\n\n## Security\n\n`paranoia`: Enable systemd security options, default `false`.\n\n    NoNewPrivileges=yes\n    PrivateDevices=yes\n    PrivateTmp=yes\n    ProtectSystem=full\n    ProtectHome=yes\n    PrivateUsers=yes\n    ProtectKernelModules=yes\n    ProtectKernelTunables=yes\n    ProtectControlGroups=yes\n    MountAPIVFS=yes\n                                                                                                    │\n`chroot`: Enable systemd [chroot](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=), default `false`.\nSets systemd `RootDirectory` is set to `current_dir`. You can also set systemd [ReadWritePaths=, ReadOnlyPaths=,\nInaccessiblePaths=](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ReadWritePaths=)\nwith the `read_write_paths`, `read_only_paths` and `inaccessible_paths` vars, respectively.\n\n\n## Distillery\n\nDistillery has largely been replaced by Elixir native releases.\nThis library works fine with it, though. `exec_start_pre` scripts\nare particularly useful in the absence of `env.sh`.\n\nConfigure the library by setting `release_system: :distillery`, e.g..\n\n```elixir\nconfig :mix_systemd,\n  release_system: :distillery,\n  exec_start_pre: [\n    # Run script as root before starting\n    [\"!\", :deploy_dir, \"/bin/deploy-sync-config-s3\"]\n  ],\n  dirs: [\n    :configuration,\n    :runtime,\n  ],\n  runtime_directory_preserve: \"yes\",\n  env_vars: [\n    # Use /run/foo for temp files\n    [\"RELEASE_MUTABLE_DIR=\", :runtime_dir],\n    # expand $CONFIGURATION_DIR in config files\n    REPLACE_OS_VARS=true,\n  ]\n```\n\nSet up\n[config providers](https://hexdocs.pm/distillery/Mix.Releases.Config.Providers.Elixir.html)\nin `rel/config.exs`:\n\n```elixir\nenvironment :prod do\n  set config_providers: [\n    {Mix.Releases.Config.Providers.Elixir, [\"${CONFIGURATION_DIR}/config.exs\"]}\n  ]\nend\n```\n\nThis reads files in Elixir config format. Instead of including your\n`prod.secret.exs` file in `prod.exs`, you can copy it to the server separately,\nand it will be read at startup.\n\nThe [TOML configuration provider](https://github.com/bitwalker/toml-elixir) works similarly:\n\n```elixir\nenvironment :prod do\n  set config_providers: [\n    {Toml.Provider, [path: \"${CONFIGURATION_DIR}/config.toml\"]},\n  ]\nend\n```\n\nAdd the TOML config provider to `mix.exs`:\n\n```elixir\n{:toml_config_provider, \"~\u003e 0.2.0\"}\n```\n\nYou can generate a file under the release with an overlay in\n`rel/config.exs`, e.g.:\n\n```elixir\nenvironment :prod do\n  set overlays: [\n    {:mkdir, \"etc\"},\n    {:copy, \"rel/etc/environment\", \"etc/environment\"},\n    # {:template, \"rel/etc/environment\", \"etc/environment\"}\n  ]\nend\n```\n\nThat results in a file that would be read by:\n\n```elixir\nenv_files: [\n  [\"-\", :current_dir, \"/etc/environment\"],\n],\n```\n\nDocumentation is here: https://hexdocs.pm/mix_systemd\n\nThis project uses the Contributor Covenant version 2.1. Check [CODE_OF_CONDUCT.md](/CODE_OF_CONDUCT.md) for more information.\n\n# Contacts\n\nI am `jakemorrison` on on the Elixir Slack and Discord, `reachfh` on Freenode\n`#elixir-lang` IRC channel. Happy to chat or help with your projects.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcogini%2Fmix_systemd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcogini%2Fmix_systemd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcogini%2Fmix_systemd/lists"}