├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── SECURITY.md ├── actions │ ├── app-exists │ │ └── action.yaml │ ├── app-inventory │ │ └── action.yaml │ ├── app-options │ │ └── action.yaml │ ├── app-tests │ │ ├── action.yaml │ │ ├── run.sh │ │ └── setup.sh │ ├── app-versions │ │ └── action.yaml │ └── release-tag │ │ └── action.yaml ├── labeler.yaml ├── labels.yaml └── workflows │ ├── app-builder.yaml │ ├── codeql.yaml │ ├── deprecate-app.yaml │ ├── label-sync.yaml │ ├── labeler.yaml │ ├── pull-request.yaml │ ├── release.yaml │ ├── retry-release.yaml │ ├── stale.yaml │ ├── test-version.yaml │ └── vulnerability-scan.yaml ├── .gitignore ├── .mise.toml ├── .renovaterc.json5 ├── .shellcheckrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── Taskfile.yaml ├── apps ├── actions-runner │ ├── Dockerfile │ ├── docker-bake.hcl │ └── tests.yaml ├── bazarr │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── beets │ ├── Dockerfile │ ├── defaults │ │ ├── config-beets.yaml │ │ └── config-betanin.toml │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── busybox │ ├── Dockerfile │ ├── docker-bake.hcl │ └── tests.yaml ├── cni-plugins │ ├── Dockerfile │ ├── docker-bake.hcl │ └── tests.yaml ├── deluge │ ├── Dockerfile │ ├── defaults │ │ └── core.conf │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── emby │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── esphome │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── home-assistant │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── irqbalance │ ├── Dockerfile │ ├── docker-bake.hcl │ └── tests.yaml ├── it-tools │ ├── Dockerfile │ ├── docker-bake.hcl │ └── tests.yaml ├── jackett │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── jbops │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── k8s-sidecar │ ├── Dockerfile │ ├── docker-bake.hcl │ └── tests.yaml ├── lidarr │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── nzbget │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── nzbhydra2 │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── opentofu-runner │ ├── Dockerfile │ ├── docker-bake.hcl │ └── tests.yaml ├── plex │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── postgres-init │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── prowlarr │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── qbittorrent │ ├── Dockerfile │ ├── defaults │ │ └── qBittorrent.conf │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── radarr │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── readarr │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── sabnzbd │ ├── Dockerfile │ ├── defaults │ │ └── sabnzbd.ini │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── smartctl-exporter │ ├── Dockerfile │ ├── docker-bake.hcl │ └── tests.yaml ├── sonarr │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── tautulli │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── theme-park │ ├── Dockerfile │ ├── docker-bake.hcl │ └── tests.yaml ├── transmission │ ├── Dockerfile │ ├── defaults │ │ └── settings.json.j2 │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml ├── webhook │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml └── whisparr │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── entrypoint.sh │ └── tests.yaml └── include └── .dockerignore /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{bash,sh}] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | [Dockerfile] 17 | indent_style = space 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.env linguist-detectable linguist-language=SHELL 3 | *.json linguist-detectable linguist-language=JSON 4 | *.json5 linguist-detectable linguist-language=JSON5 5 | *.hcl linguist-detectable linguist-language=HCL 6 | *.md linguist-detectable linguist-language=MARKDOWN 7 | *.sh linguist-detectable linguist-language=SHELL 8 | *.toml linguist-detectable linguist-language=TOML 9 | *.yml linguist-detectable linguist-language=YAML 10 | *.yaml linguist-detectable linguist-language=YAML 11 | *.yaml.j2 linguist-detectable linguist-language=YAML 12 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 2 | * @buroa @onedr0p @bjw-s 3 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at contact@buhl.casa. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series 85 | of actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or 92 | permanent ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within 112 | the community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, available at 118 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | https://www.contributor-covenant.org/faq. Translations are available at 127 | https://www.contributor-covenant.org/translations. 128 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | First off, thank you for taking the time to contribute!🎉💪 4 | 5 | The following is a set of guidelines for contributing. These are mostly guidelines, not rules. 6 | Use your best judgment, and feel free to propose changes to this document in a pull request. 7 | 8 | ## Table of contents 9 | 10 | - [Contribution Guidelines](#contribution-guidelines) 11 | - [Table of contents](#table-of-contents) 12 | - [Why?](#why) 13 | - [Before Getting Started](#before-getting-started) 14 | - [Learn about our code of conduct](#learn-about-our-code-of-conduct) 15 | - [Got a Question or Problem?](#got-a-question-or-problem) 16 | - [Different contributions](#different-contributions) 17 | - [Found a bug?](#found-a-bug) 18 | - [Found a security vulnerability?](#found-a-security-vulnerability) 19 | - [Missing a Feature?](#missing-a-feature) 20 | - [Want to improve the documentation?](#want-to-improve-the-documentation) 21 | - [Submission Guidelines](#submission-guidelines) 22 | - [Submitting an Issue](#submitting-an-issue) 23 | - [Naming a Pull Request (PR)](#naming-a-pull-request-pr) 24 | - [Submitting a Pull Request](#submitting-a-pull-request) 25 | - [Reviewing a Pull Request](#reviewing-a-pull-request) 26 | - [Your First Contribution](#your-first-contribution) 27 | 28 | ## Why? 29 | 30 | Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open-source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests. 31 | 32 | ## Before Getting Started 33 | 34 | ### Learn about our code of conduct 35 | 36 | See the [code of conduct](./CODE_OF_CONDUCT.md). 37 | 38 | ### Got a Question or Problem? 39 | 40 | If you have questions about this project please ask the question by submitting an issue 41 | in this repository. 42 | 43 | ## Different contributions 44 | 45 | There are many ways to contribute, from writing tutorials, improving the documentation, submitting bug reports and feature requests, or writing code that can be incorporated into the project itself. 46 | 47 | ### Found a bug? 48 | 49 | If you find a bug in the source code or a mistake in the documentation, you can help us by [submitting an issue](#submitting-an-issue) to our GitHub Repository. 50 | 51 | ### Found a security vulnerability? 52 | 53 | If you discover a vulnerability in our software, please refer to the [security policy](./SECURITY.md) and report it appropriately. 54 | Do not submit an issue, unless asked to. 55 | 56 | ### Missing a Feature? 57 | 58 | You can request a new feature by [submitting an issue](#submitting-an-issue) to our GitHub Repository. If you would like to implement a new feature, please consider the size of the change in order to determine the right steps to proceed: 59 | 60 | For a Major Feature, first, open an issue and outline your proposal so that it can be discussed. This process allows us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project. 61 | 62 | Note: Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major feature. 63 | 64 | Small Features can be crafted and directly submitted as a Pull Request. 65 | 66 | ### Want to improve the documentation? 67 | 68 | If you want to help improve the docs, it's a good idea to let others know what you're working on to minimize duplication of effort. 69 | Create a new issue (or comment on a related existing one) to let others know what you're working on. 70 | 71 | ## Submission Guidelines 72 | 73 | ### Submitting an Issue 74 | 75 | Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available. 76 | 77 | We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs, we require that you provide minimal reproduction. Having a minimal reproducible scenario gives us a wealth of important information without going back and forth with you with additional questions. 78 | 79 | A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem. 80 | 81 | We require minimal reproduction to save maintainers time and ultimately be able to fix more bugs. Often, developers find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it. 82 | 83 | Unfortunately, we are not able to investigate/fix bugs without minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced. 84 | 85 | You can file new issues by selecting from our new issue templates and filling out the issue template. 86 | 87 | ### Naming a Pull Request (PR) 88 | 89 | The title of your Pull Request (PR) should follow the style of [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). Not only does this present a standardized categorization of the kind of work done on a pull request, but it also instructs the release workflow to increment the correct level of the version according to the rules of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 90 | 91 | The format of the title of the pull request is this: 92 | 93 | `[(optional scope)][!]: ` 94 | 95 | The `` of the pull request is one of these, taken from [conventional commit types](https://github.com/commitizen/conventional-commit-types): 96 | 97 | - `feat:` a new feature 98 | - `fix:` a bug fix 99 | - `docs:` documentation only changes 100 | - `style:` changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 101 | - `refactor:` a code change that neither fixes a bug nor adds a feature 102 | - `perf:` a code change that improves performance 103 | - `test:` adding missing tests or correcting existing tests 104 | - `build:` changes that affect the build system or external dependencies 105 | - `ci:` changes to our CI configuration files and scripts 106 | - `chore:` other changes that don't modify source or test files 107 | - `revert:` reverts a previous commit 108 | 109 | An exclamation mark `!` is added to the type if the change is not backwards compatible. This should only be added to `feat` or `fix`. 110 | 111 | > [!NOTE] 112 | > We do not enforce conventional commits for individual commit messages, only for the title of your pull request. 113 | 114 | Examples: 115 | 116 | - `feat: add required-tool to devcontainer` 117 | 118 | This pull request adds the "required-tool" to the devcontainer because everybody want to use it. 119 | 120 | - `fix!: escape fe ff in binary ports` 121 | 122 | This pull request fixes binary ports, and indicates that this is a backwards-incompatible change. 123 | 124 | > [!TIP] 125 | > If your work consists of a single commit, creating a pull request will default to the name of that commit. If you use conventional commit style for that single commit, your pull request already has the correct name. 126 | 127 | ### Submitting a Pull Request 128 | 129 | Before you submit your pull request consider the following guidelines: 130 | 131 | 1. Search the GitHub Repository for an open or closed PR that relates to your submission. 132 | You don't want to duplicate existing efforts. 133 | 134 | 1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add. 135 | Discussing the design upfront helps to ensure that we're ready to accept your work. 136 | 137 | 1. Fork the repository. 138 | 139 | 1. Make your changes in a new git branch: 140 | 141 | ```shell 142 | git checkout -b feature/my-fix-branch main 143 | ``` 144 | 145 | 1. Create your patch, include tests if necessary. 146 | 147 | 1. Ensure that all tests pass. 148 | 149 | 1. Commit your changes using a descriptive commit message. 150 | 151 | ```shell 152 | git commit -s -m 'Awesome commit message' 153 | ``` 154 | 155 | Note: the optional commit `-a` command-line option will automatically "add" and "rm" edited files. 156 | 157 | 1. Push your branch to your GitHub fork: 158 | 159 | ```shell 160 | git push origin feature/my-fix-branch 161 | ``` 162 | 163 | 1. In GitHub, send a pull request to merge from the branch on your fork to the main branch in the upstream. 164 | 165 | 1. After your pull request is merged, make sure that your branch and/or fork are deleted. 166 | 167 | ### Reviewing a Pull Request 168 | 169 | Anyone can review pull requests, we encourage others to review each other's work, however, only the maintainers can approve a pull request. 170 | Pull Requests often require several approvals from maintainers, before being able to merge it. 171 | 172 | ## Your First Contribution 173 | 174 | Unsure where to begin contributing? You can start by looking through good-first-issue and help-wanted issues: 175 | "Good first issue" issues - issues that should only require a few lines of code, and a test or two. 176 | "Help wanted" issues - issues that should be a bit more involved than good-first-issue issues. 177 | 178 | Working on your first Pull Request? You can learn how from this _free_ series, [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). If you prefer to read through some tutorials, visit and 179 | 180 | At this point, you're ready to make your changes! Feel free to ask for help; everyone is a beginner at first :smile_cat: 181 | 182 | If a maintainer asks you to "rebase" your PR, they're saying that a lot of code has changed and that you need to update your branch so it's easier to merge. 183 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you find a significant vulnerability, or evidence of one, please report it privately. 6 | 7 | Vulnerabilities should be reported using [GitHub's mechanism for privately reporting a vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability). Under the 8 | [main repository's security tab](https://github.com/home-operations/containers/security), click "Report a vulnerability" to open the advisory form. 9 | 10 | A member of this organization will triage the reported vulnerability and if the vulnerability is accepted a security advisory will be published and all further communication will be done via that security advisory. 11 | -------------------------------------------------------------------------------- /.github/actions/app-exists/action.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-action.json 3 | name: Application Exists 4 | description: Return true or false if the application exists in the container registry 5 | 6 | inputs: 7 | app: 8 | description: Application Name 9 | required: true 10 | 11 | outputs: 12 | exists: 13 | description: Application Exists 14 | value: ${{ steps.application.outputs.exists }} 15 | 16 | runs: 17 | using: composite 18 | steps: 19 | - name: Application Exists 20 | id: application 21 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 22 | with: 23 | script: | 24 | const applicationName = '${{ inputs.app }}'; 25 | 26 | const { data: user } = await github.rest.users.getByUsername({ 27 | username: context.repo.owner, 28 | }); 29 | 30 | let container; 31 | if (user.type === 'Organization') { 32 | try { 33 | const { data } = await github.rest.packages.getPackageForOrganization({ 34 | package_type: 'container', 35 | package_name: applicationName, 36 | org: context.repo.owner, 37 | }); 38 | container = data; 39 | } catch (error) { 40 | if (error.status !== 404) { 41 | throw error; 42 | } 43 | } 44 | } else { 45 | try { 46 | const { data } = await github.rest.packages.getPackageForUser({ 47 | package_type: 'container', 48 | package_name: applicationName, 49 | username: context.repo.owner, 50 | }); 51 | container = data; 52 | } catch (error) { 53 | if (error.status !== 404) { 54 | throw error; 55 | } 56 | } 57 | } 58 | 59 | core.setOutput('exists', typeof container !== "undefined"); 60 | -------------------------------------------------------------------------------- /.github/actions/app-inventory/action.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-action.json 3 | name: Application Inventory 4 | description: Return a JSON array of app names from the apps directory 5 | 6 | outputs: 7 | apps: 8 | description: Application Inventory 9 | value: ${{ steps.inventory.outputs.apps }} 10 | 11 | runs: 12 | using: composite 13 | steps: 14 | - name: Application Inventory 15 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 16 | id: inventory 17 | with: 18 | script: | 19 | const fs = require('fs'); 20 | const path = require('path'); 21 | 22 | function scanDirectory(path) { 23 | try { 24 | const entries = fs.readdirSync(path, { withFileTypes: true }); 25 | const folderNames = entries 26 | .filter((entry) => entry.isDirectory()) 27 | .map((entry) => entry.name); 28 | return folderNames; 29 | } catch (error) { 30 | core.setFailed(`Error reading directory ${path}: ${error.message}`); 31 | return []; 32 | } 33 | } 34 | 35 | const appsDirectory = path.resolve(process.cwd(), 'apps'); 36 | const apps = scanDirectory(appsDirectory).sort(); 37 | core.setOutput('apps', JSON.stringify(apps)); 38 | -------------------------------------------------------------------------------- /.github/actions/app-options/action.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-action.json 3 | name: Application Options 4 | description: Takes an app and returns various fields from its docker-bake.hcl file 5 | 6 | inputs: 7 | app: 8 | description: Application Name 9 | required: true 10 | 11 | outputs: 12 | platforms: 13 | description: Platforms 14 | value: ${{ steps.options.outputs.platforms }} 15 | source: 16 | description: Source 17 | value: ${{ steps.options.outputs.source }} 18 | version: 19 | description: Version 20 | value: ${{ steps.options.outputs.version }} 21 | 22 | runs: 23 | using: composite 24 | steps: 25 | - name: Application Options 26 | id: options 27 | shell: bash 28 | working-directory: ./apps/${{ inputs.app }} 29 | run: | 30 | PLATFORMS=$(\ 31 | docker buildx bake image-all --print --progress=quiet \ 32 | | jq --raw-output --compact-output '.target."image-all".platforms' \ 33 | ) 34 | SOURCE=$(\ 35 | docker buildx bake --list type=variables,format=json --progress=quiet \ 36 | | jq --raw-output '.[] | select(.name == "SOURCE") | .value' \ 37 | ) 38 | VERSION=$(\ 39 | docker buildx bake --list type=variables,format=json --progress=quiet \ 40 | | jq --raw-output '.[] | select(.name == "VERSION") | .value' \ 41 | ) 42 | 43 | echo "platforms=${PLATFORMS}" >> $GITHUB_OUTPUT 44 | echo "source=${SOURCE}" >> $GITHUB_OUTPUT 45 | echo "version=${VERSION}" >> $GITHUB_OUTPUT 46 | -------------------------------------------------------------------------------- /.github/actions/app-tests/action.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-action.json 3 | name: Application Tests 4 | description: Takes an app, container image and token then runs container tests 5 | 6 | inputs: 7 | app: 8 | description: Application Name 9 | required: true 10 | image: 11 | description: Image 12 | required: true 13 | token: 14 | description: GitHub Token 15 | required: true 16 | 17 | runs: 18 | using: composite 19 | steps: 20 | - name: Setup Actions Script Path 21 | shell: bash 22 | run: | 23 | echo "${{ github.action_path }}" >> $GITHUB_PATH 24 | 25 | - name: Setup Testing Tools 26 | shell: bash 27 | env: 28 | GITHUB_TOKEN: ${{ inputs.token }} 29 | run: | 30 | setup.sh "${{ inputs.app }}" 31 | 32 | - name: Run Tests 33 | shell: bash 34 | run: | 35 | run.sh "${{ inputs.app }}" "${{ inputs.image }}" 36 | -------------------------------------------------------------------------------- /.github/actions/app-tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | APP="${1:?}" 5 | IMAGE="${2:?}" 6 | 7 | if [[ -x "$(command -v container-structure-test)" ]]; then 8 | container-structure-test test --image "${IMAGE}" --config "./apps/${APP}/tests.yaml" 9 | elif [[ -x "$(command -v goss)" && -x "$(command -v dgoss)" ]]; then 10 | export GOSS_FILE="./apps/${APP}/tests.yaml" 11 | export GOSS_OPTS="--retry-timeout 60s --sleep 1s" 12 | dgoss run "${IMAGE}" 13 | else 14 | echo "No testing tool found. Exiting." 15 | exit 1 16 | fi 17 | -------------------------------------------------------------------------------- /.github/actions/app-tests/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | APP="${1:?}" 5 | 6 | if yq --exit-status '.schemaVersion' "./apps/${APP}/tests.yaml" &>/dev/null; then 7 | gh release download --repo GoogleContainerTools/container-structure-test --pattern "*-linux-$(dpkg --print-architecture)" --output /usr/local/bin/container-structure-test 8 | chmod +x /usr/local/bin/container-structure-test 9 | else 10 | gh release download --repo goss-org/goss --pattern "*-linux-$(dpkg --print-architecture)" --output /usr/local/bin/goss 11 | chmod +x /usr/local/bin/goss 12 | gh release download --repo goss-org/goss --pattern "dgoss" --output /usr/local/bin/dgoss 13 | chmod +x /usr/local/bin/dgoss 14 | fi 15 | -------------------------------------------------------------------------------- /.github/actions/app-versions/action.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-action.json 3 | name: Application Versions 4 | description: Takes an upstream version string and returns various version types 5 | 6 | inputs: 7 | upstream-version: 8 | description: Upstream Version 9 | required: true 10 | 11 | outputs: 12 | is-valid-semver: 13 | description: If version is valid semantic versioning 14 | value: ${{ steps.versions.outputs.is-valid-semver }} 15 | semantic: 16 | description: Semantic Version 17 | value: ${{ steps.versions.outputs.semantic }} 18 | raw: 19 | description: Raw Version 20 | value: ${{ steps.versions.outputs.raw }} 21 | upstream: 22 | description: Upstream Version 23 | value: ${{ steps.versions.outputs.upstream }} 24 | 25 | runs: 26 | using: composite 27 | steps: 28 | - name: Setup Node 29 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 30 | with: 31 | node-version: 22.x 32 | 33 | - name: Install Semver 34 | shell: bash 35 | run: npm install semver 36 | 37 | - name: Application Versions 38 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 39 | id: versions 40 | with: 41 | script: | 42 | const semver = require('semver'); 43 | 44 | // Strip the v prefix and pre-release info 45 | function sanitize(version) { 46 | return version.replace(/^v/, '').split('-')[0]; 47 | } 48 | 49 | function calver() { 50 | const now = new Date(); 51 | return `${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}`; 52 | } 53 | 54 | const upstreamVersion = '${{ inputs.upstream-version }}'; 55 | const strictSemverRegex = /^v?(\d+(\.\d+)?(\.\d+)?)/; 56 | 57 | const parsedVersion = strictSemverRegex.exec(upstreamVersion); 58 | const isValidSemver = parsedVersion !== null; 59 | const parsedSemver = isValidSemver ? semver.coerce(parsedVersion[0], {loose: true }) : null; 60 | const semanticVersion = isValidSemver ? `${parsedSemver.major}.${parsedSemver.minor}.${parsedSemver.patch}` : calver(); 61 | const rawVersion = isValidSemver ? sanitize(upstreamVersion) : upstreamVersion; 62 | 63 | core.setOutput('is-valid-semver', isValidSemver); 64 | core.setOutput('semantic', semanticVersion); 65 | core.setOutput('raw', rawVersion); 66 | core.setOutput('upstream', upstreamVersion); 67 | -------------------------------------------------------------------------------- /.github/actions/release-tag/action.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-action.json 3 | name: Release Tag 4 | description: Return the next release tag based on the current date and the latest release tag 5 | 6 | inputs: 7 | token: 8 | description: GitHub Token 9 | required: true 10 | 11 | outputs: 12 | tag: 13 | description: Release Tag 14 | value: ${{ steps.release.outputs.tag }} 15 | 16 | runs: 17 | using: composite 18 | steps: 19 | - name: Get Release Tag 20 | id: release 21 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 22 | with: 23 | github-token: ${{ inputs.token }} 24 | script: | 25 | const { data: releases } = await github.rest.repos.listReleases({ 26 | owner: context.repo.owner, 27 | repo: context.repo.repo, 28 | per_page: 1, 29 | }); 30 | 31 | const prevTag = releases[0]?.tag_name || "0.0.0"; // Default to "0.0.0" if no tags exist 32 | const [prevMajor, prevMinor, prevPatch] = prevTag.split('.').map(Number); 33 | const now = new Date(); 34 | const nextMajorMinor = `${now.getFullYear()}.${now.getMonth() + 1}`; // Months are 0-indexed in JavaScript 35 | const nextPatch = `${prevMajor}.${prevMinor}` === nextMajorMinor ? prevPatch + 1 : 0; 36 | 37 | core.setOutput('tag', `${nextMajorMinor}.${nextPatch}`); 38 | -------------------------------------------------------------------------------- /.github/labeler.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | area/apps: 3 | - changed-files: 4 | - any-glob-to-any-file: 5 | - apps/**/* 6 | - include/**/* 7 | area/github: 8 | - changed-files: 9 | - any-glob-to-any-file: 10 | - .github/**/* 11 | area/mise: 12 | - changed-files: 13 | - any-glob-to-any-file: 14 | - .mise.toml 15 | area/renovate: 16 | - changed-files: 17 | - any-glob-to-any-file: 18 | - .renovate/**/* 19 | - .renovaterc.json5 20 | area/taskfile: 21 | - changed-files: 22 | - any-glob-to-any-file: 23 | - .taskfiles/**/* 24 | - Taskfile.yaml 25 | -------------------------------------------------------------------------------- /.github/labels.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Areas 3 | - name: area/apps 4 | color: 0e8a16 5 | - name: area/github 6 | color: 0e8a16 7 | - name: area/mise 8 | color: 0e8a16 9 | - name: area/renovate 10 | color: 0e8a16 11 | - name: area/taskfile 12 | color: 0e8a16 13 | # Semantic Types 14 | - name: type/digest 15 | color: ffeC19 16 | - name: type/patch 17 | color: ffeC19 18 | - name: type/minor 19 | color: ff9800 20 | - name: type/major 21 | color: f6412d 22 | # Uncategorized 23 | - name: stale 24 | color: c5def5 25 | -------------------------------------------------------------------------------- /.github/workflows/app-builder.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: Application Builder 4 | 5 | on: 6 | workflow_call: 7 | inputs: 8 | app: 9 | type: string 10 | description: Application Name 11 | required: true 12 | release: 13 | type: boolean 14 | description: Release 15 | required: true 16 | 17 | jobs: 18 | plan: 19 | name: Plan 20 | runs-on: ubuntu-latest 21 | outputs: 22 | app-exists: ${{ steps.app-exists.outputs.exists }} 23 | platforms: ${{ steps.app-options.outputs.platforms }} 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 27 | with: 28 | persist-credentials: false 29 | 30 | - name: Check For Exising Application 31 | uses: ./.github/actions/app-exists 32 | id: app-exists 33 | with: 34 | app: ${{ inputs.app }} 35 | 36 | - name: Get Application Options 37 | id: app-options 38 | uses: ./.github/actions/app-options 39 | with: 40 | app: ${{ inputs.app }} 41 | 42 | - name: Get Application Versions 43 | uses: ./.github/actions/app-versions 44 | id: app-versions 45 | with: 46 | upstream-version: ${{ steps.app-options.outputs.version }} 47 | 48 | - name: Build Application Metadata 49 | uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 50 | id: meta 51 | env: 52 | DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index 53 | with: 54 | flavor: latest=false 55 | images: | 56 | ghcr.io/${{ github.repository_owner }}/${{ inputs.app }} 57 | tags: | 58 | type=semver,pattern={{version}},value=${{ steps.app-versions.outputs.semantic }} 59 | type=semver,pattern={{major}}.{{minor}},value=${{ steps.app-versions.outputs.semantic }},enable=${{ steps.app-versions.outputs.is-valid-semver }} 60 | type=semver,pattern={{major}},value=${{ steps.app-versions.outputs.semantic }},enable=${{ steps.app-versions.outputs.is-valid-semver }} 61 | type=raw,value=${{ steps.app-versions.outputs.raw }},enable=${{ steps.app-versions.outputs.is-valid-semver }} 62 | type=raw,value=rolling 63 | 64 | - name: Upload Bake Metadata 65 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 66 | with: 67 | name: ${{ inputs.app }}-bake-metadata 68 | path: ${{ steps.meta.outputs.bake-file }} 69 | if-no-files-found: error 70 | retention-days: 1 71 | 72 | build: 73 | name: Build (${{ matrix.platform }}) 74 | needs: ["plan"] 75 | strategy: 76 | fail-fast: false 77 | matrix: 78 | platform: ${{ fromJson(needs.plan.outputs.platforms) }} 79 | runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} 80 | steps: 81 | - name: Checkout 82 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 83 | with: 84 | persist-credentials: false 85 | 86 | - name: Get Target Architecture 87 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 88 | id: target 89 | with: 90 | script: | 91 | core.setOutput('arch', '${{ matrix.platform }}'.split('/').pop()); 92 | 93 | - name: Get Application Options 94 | id: app-options 95 | uses: ./.github/actions/app-options 96 | with: 97 | app: ${{ inputs.app }} 98 | 99 | - name: Login to GitHub Container Registry 100 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 101 | with: 102 | registry: ghcr.io 103 | username: ${{ github.actor }} 104 | password: ${{ github.token }} 105 | 106 | - name: Download Bake Metadata 107 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 108 | with: 109 | name: ${{ inputs.app }}-bake-metadata 110 | path: ${{ runner.temp }} 111 | 112 | - name: Setup Docker Buildx 113 | uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 114 | 115 | - name: Add Include Directory to Build Context 116 | run: | 117 | rsync -a --ignore-existing --progress ./include/ ./apps/${{ inputs.app }}/ 118 | 119 | - name: Build Application 120 | uses: docker/bake-action@37816e747588cb137173af99ab33873600c46ea8 # v6.8.0 121 | id: bake 122 | with: 123 | files: | 124 | ./docker-bake.hcl 125 | cwd://${{ runner.temp }}/docker-metadata-action-bake.json 126 | set: | 127 | *.args.VENDOR=${{ github.repository_owner }} 128 | *.cache-from=${{ format('type=registry,ref=ghcr.io/{0}/build_cache:{1}-{2},mode=max', github.repository_owner, inputs.app, steps.target.outputs.arch) }} 129 | *.cache-to=${{ inputs.release && format('type=registry,ref=ghcr.io/{0}/build_cache:{1}-{2},mode=max,compression=zstd,force-compression=true', github.repository_owner, inputs.app, steps.target.outputs.arch) || '' }} 130 | *.labels.org.opencontainers.image.title=${{ inputs.app }} 131 | *.labels.org.opencontainers.image.url=https://ghcr.io/${{ github.repository_owner }}/${{ inputs.app }} 132 | *.labels.org.opencontainers.image.version=${{ steps.app-options.outputs.version }} 133 | *.labels.org.opencontainers.image.revision=${{ github.sha }} 134 | *.labels.org.opencontainers.image.vendor=${{ github.repository_owner }} 135 | ${{ inputs.release && format('*.output=type=image,name=ghcr.io/{0}/{1},push-by-digest=true,name-canonical=true,push=true', github.repository_owner, inputs.app) || '*.output=type=docker' }} 136 | *.platform=${{ matrix.platform }} 137 | *.tags= 138 | source: . 139 | targets: image 140 | workdir: ./apps/${{ inputs.app }} 141 | 142 | - if: ${{ ! inputs.release }} 143 | name: Run Application Tests 144 | uses: ./.github/actions/app-tests 145 | with: 146 | app: ${{ inputs.app }} 147 | image: ${{ fromJSON(steps.bake.outputs.metadata).image['containerimage.config.digest'] }} 148 | token: ${{ github.token }} 149 | 150 | - if: ${{ inputs.release }} 151 | name: Export Digest 152 | run: | 153 | mkdir -p ${{ runner.temp }}/digests 154 | DIGEST="${{ fromJSON(steps.bake.outputs.metadata).image['containerimage.digest'] }}" 155 | touch "${{ runner.temp }}/digests/${DIGEST#sha256:}" 156 | 157 | - if: ${{ inputs.release }} 158 | name: Upload Digest 159 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 160 | with: 161 | name: ${{ inputs.app }}-digests-${{ steps.target.outputs.arch }} 162 | path: ${{ runner.temp }}/digests/* 163 | if-no-files-found: error 164 | retention-days: 1 165 | 166 | release: 167 | if: ${{ inputs.release }} 168 | name: Release 169 | runs-on: ubuntu-latest 170 | needs: ["build"] 171 | outputs: 172 | digest: ${{ steps.digest.outputs.digest }} 173 | steps: 174 | - name: Checkout 175 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 176 | with: 177 | persist-credentials: false 178 | 179 | - name: Download Bake Metadata 180 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 181 | with: 182 | name: ${{ inputs.app }}-bake-metadata 183 | path: ${{ runner.temp }} 184 | 185 | - name: Download Digests 186 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 187 | with: 188 | path: ${{ runner.temp }}/digests 189 | pattern: ${{ inputs.app }}-digests-* 190 | merge-multiple: true 191 | 192 | - name: Login to GitHub Container Registry 193 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 194 | with: 195 | registry: ghcr.io 196 | username: ${{ github.actor }} 197 | password: ${{ github.token }} 198 | 199 | - name: Create Manifest List and Push 200 | working-directory: ${{ runner.temp }}/digests 201 | run: | 202 | docker buildx imagetools create \ 203 | $(jq --raw-output --compact-output '.target."docker-metadata-action".tags | map(select(startswith("ghcr.io/${{ github.repository_owner }}/${{ inputs.app }}")) | "-t " + .) | join(" ")' ${{ runner.temp }}/docker-metadata-action-bake.json) \ 204 | $(printf 'ghcr.io/${{ github.repository_owner }}/${{ inputs.app }}@sha256:%s ' *) 205 | 206 | - name: Inspect Image 207 | run: | 208 | docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ inputs.app }}:$(jq --raw-output '.target."docker-metadata-action".args.DOCKER_META_VERSION' ${{ runner.temp }}/docker-metadata-action-bake.json) 209 | 210 | - name: Export Digest 211 | id: digest 212 | run: | 213 | TAG=$(jq --raw-output '.target."docker-metadata-action".args.DOCKER_META_VERSION' ${{ runner.temp }}/docker-metadata-action-bake.json) 214 | DIGEST=$(docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ inputs.app }}:${TAG} --format '{{ json . }}' | jq --raw-output '.manifest.digest') 215 | echo "digest=${DIGEST}" >> $GITHUB_OUTPUT 216 | 217 | attest: 218 | if: ${{ inputs.release }} 219 | name: Attest 220 | needs: ["release"] 221 | runs-on: ubuntu-latest 222 | steps: 223 | - name: Login to GitHub Container Registry 224 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 225 | with: 226 | registry: ghcr.io 227 | username: ${{ github.actor }} 228 | password: ${{ github.token }} 229 | 230 | - name: Upload Dependency Snapshot 231 | uses: anchore/sbom-action@e11c554f704a0b820cbf8c51673f6945e0731532 # v0.20.0 232 | with: 233 | dependency-snapshot: true 234 | image: ghcr.io/${{ github.repository_owner }}/${{ inputs.app }}@${{ needs.release.outputs.digest }} 235 | 236 | - name: Attestation 237 | uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0 238 | with: 239 | push-to-registry: true 240 | subject-name: ghcr.io/${{ github.repository_owner }}/${{ inputs.app }} 241 | subject-digest: ${{ needs.release.outputs.digest }} 242 | 243 | - name: Verify Attestation 244 | env: 245 | GITHUB_TOKEN: ${{ github.token }} 246 | run: | 247 | gh attestation verify --repo ${{ github.repository }} oci://ghcr.io/${{ github.repository_owner }}/${{ inputs.app }}@${{ needs.release.outputs.digest }} 248 | 249 | announce: 250 | if: ${{ inputs.release && needs.plan.outputs.app-exists != 'true' }} 251 | name: Announce 252 | needs: ["plan", "attest"] 253 | runs-on: ubuntu-latest 254 | steps: 255 | - name: Checkout 256 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 257 | with: 258 | persist-credentials: false 259 | 260 | - name: Generate Token 261 | uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 262 | id: app-token 263 | with: 264 | app-id: ${{ secrets.BOT_APP_ID }} 265 | private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} 266 | 267 | - name: Get Application Options 268 | id: app-options 269 | uses: ./.github/actions/app-options 270 | with: 271 | app: ${{ inputs.app }} 272 | 273 | - name: Get Release Tag 274 | uses: ./.github/actions/release-tag 275 | id: release 276 | with: 277 | token: ${{ steps.app-token.outputs.token }} 278 | 279 | - name: Create Release 280 | uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1.16.0 281 | with: 282 | body: | 283 | > [!NOTE] 284 | > A new application has been added. 285 | 286 | ## 📦 Application 287 | 288 | **Name**: [${{ inputs.app }}](https://github.com/${{ github.repository }}/pkgs/container/${{ inputs.app }}) 289 | **Source**: <${{ steps.app-options.outputs.source }}> 290 | tag: ${{ steps.release.outputs.tag }} 291 | token: ${{ steps.app-token.outputs.token }} 292 | 293 | notify: 294 | if: ${{ inputs.release && !cancelled() }} 295 | needs: ["plan", "build", "release", "attest", "announce"] 296 | name: Notify 297 | runs-on: ubuntu-latest 298 | steps: 299 | - if: ${{ contains(needs.*.result, 'failure') }} 300 | name: Checkout 301 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 302 | with: 303 | persist-credentials: false 304 | 305 | - if: ${{ contains(needs.*.result, 'failure') }} 306 | name: Get Application Options 307 | id: app-options 308 | uses: ./.github/actions/app-options 309 | with: 310 | app: ${{ inputs.app }} 311 | 312 | - if: ${{ contains(needs.*.result, 'failure') }} 313 | name: Send Discord Webhook 314 | uses: sarisia/actions-status-discord@5ddd3b114a98457dd80a39b2f00b6a998cd69008 # v1.15.3 315 | with: 316 | color: "0xFF0000" 317 | description: | 318 | Application: `${{ inputs.app }}` 319 | Version: `${{ steps.app-options.outputs.version }}` 320 | [Rebuild](${{ github.server_url }}/${{ github.repository }}/actions/workflows/release.yaml) 321 | nodetail: true 322 | title: Container build failed 323 | username: GitHub Actions 324 | url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 325 | webhook: ${{ secrets.DISCORD_WEBHOOK }} 326 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: CodeQL 4 | 5 | on: 6 | schedule: 7 | - cron: "30 1 * * *" 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | analyze: 15 | name: Analyze (${{ matrix.language }}) 16 | runs-on: ubuntu-latest 17 | permissions: 18 | security-events: write 19 | packages: read 20 | actions: read 21 | contents: read 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | include: 26 | - language: actions 27 | build-mode: none 28 | source-root: . 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 32 | with: 33 | persist-credentials: false 34 | 35 | - name: Initialize CodeQL 36 | uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 37 | with: 38 | languages: ${{ matrix.language }} 39 | build-mode: ${{ matrix.build-mode }} 40 | source-root: ${{ matrix.source-root }} 41 | 42 | - name: Perform CodeQL Analysis 43 | uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 44 | with: 45 | category: language:${{ matrix.language }} 46 | -------------------------------------------------------------------------------- /.github/workflows/deprecate-app.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: Deprecate Application 4 | 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | app: 9 | type: string 10 | description: Application Name 11 | required: true 12 | reason: 13 | type: string 14 | description: Deprecation Reason 15 | required: true 16 | release: 17 | type: boolean 18 | description: Create Release 19 | default: true 20 | 21 | permissions: 22 | contents: read 23 | 24 | jobs: 25 | plan: 26 | name: Plan (${{ inputs.app }}) 27 | runs-on: ubuntu-latest 28 | outputs: 29 | pull-number: ${{ steps.pr.outputs.pull-request-number }} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 33 | with: 34 | persist-credentials: false 35 | 36 | - name: Generate Token 37 | uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 38 | id: app-token 39 | with: 40 | app-id: ${{ secrets.BOT_APP_ID }} 41 | private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} 42 | 43 | - name: Delete App Directory 44 | run: | 45 | rm -rf ./apps/${{ inputs.app }} 46 | 47 | - name: Create Pull Request 48 | uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 49 | id: pr 50 | with: 51 | body: | 52 | **Reason**: ${{ inputs.reason }} 53 | branch: deprecate/${{ inputs.app }} 54 | commit-message: "release(${{ inputs.app }})!: deprecate container image" 55 | sign-commits: true 56 | signoff: true 57 | title: "release(${{ inputs.app }})!: deprecate container image" 58 | token: ${{ steps.app-token.outputs.token }} 59 | 60 | merge: 61 | if: ${{ inputs.release }} 62 | name: Merge (${{ inputs.app }}) 63 | needs: ["plan"] 64 | runs-on: ubuntu-latest 65 | steps: 66 | - name: Generate Token 67 | uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 68 | id: app-token 69 | with: 70 | app-id: ${{ secrets.BOT_APP_ID }} 71 | private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} 72 | 73 | - name: Merge Pull Request 74 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 75 | with: 76 | github-token: ${{ steps.app-token.outputs.token }} 77 | script: | 78 | await github.rest.pulls.merge({ 79 | owner: context.repo.owner, 80 | repo: context.repo.repo, 81 | pull_number: ${{ needs.plan.outputs.pull-number }}, 82 | merge_method: 'squash', 83 | }); 84 | 85 | announce: 86 | if: ${{ inputs.release }} 87 | name: Announce (${{ inputs.app }}) 88 | needs: ["merge"] 89 | runs-on: ubuntu-latest 90 | steps: 91 | - name: Checkout 92 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 93 | with: 94 | persist-credentials: false 95 | 96 | - name: Generate Token 97 | uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 98 | id: app-token 99 | with: 100 | app-id: ${{ secrets.BOT_APP_ID }} 101 | private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} 102 | 103 | - name: Get Release Tag 104 | uses: ./.github/actions/release-tag 105 | id: release 106 | with: 107 | token: ${{ steps.app-token.outputs.token }} 108 | 109 | - name: Create Release 110 | uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1.16.0 111 | with: 112 | body: | 113 | > [!WARNING] 114 | > An application has been deprecated and will no longer receive updates. 115 | > After **6 months** from now, its image will be **removed** from our container registry. 116 | 117 | ## 📦 Application 118 | 119 | **Name**: ${{ inputs.app }} 120 | **Reason**: ${{ inputs.reason }} 121 | tag: ${{ steps.release.outputs.tag }} 122 | token: ${{ steps.app-token.outputs.token }} 123 | -------------------------------------------------------------------------------- /.github/workflows/label-sync.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: Label Sync 4 | 5 | on: 6 | schedule: 7 | - cron: "30 1 * * *" 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | label-sync: 15 | name: Label Sync 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 20 | with: 21 | persist-credentials: false 22 | 23 | - name: Generate Token 24 | uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 25 | id: app-token 26 | with: 27 | app-id: ${{ secrets.BOT_APP_ID }} 28 | private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} 29 | 30 | - name: Install Task 31 | uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 32 | with: 33 | repo-token: ${{ steps.app-token.outputs.token }} 34 | version: 3.x 35 | 36 | - name: Generate Label Config 37 | run: task generate-label-config --force 38 | 39 | - name: Sync Labels 40 | uses: EndBug/label-sync@52074158190acb45f3077f9099fea818aa43f97a # v2.3.3 41 | with: 42 | token: ${{ steps.app-token.outputs.token }} 43 | config-file: .github/labels.yaml 44 | delete-other-labels: true 45 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: Labeler 4 | 5 | on: 6 | # zizmor: ignore[dangerous-triggers] 7 | pull_request_target: 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | labeler: 14 | name: Labeler 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: read 18 | pull-requests: write 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 22 | with: 23 | persist-credentials: false 24 | 25 | - name: Generate Token 26 | uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 27 | id: app-token 28 | with: 29 | app-id: ${{ secrets.BOT_APP_ID }} 30 | private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} 31 | 32 | - name: Install Task 33 | uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 34 | with: 35 | repo-token: ${{ steps.app-token.outputs.token }} 36 | version: 3.x 37 | 38 | - name: Generate Label Config 39 | run: task generate-label-config --force 40 | 41 | - name: Labeler 42 | uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0 43 | with: 44 | repo-token: ${{ steps.app-token.outputs.token }} 45 | configuration-path: .github/labeler.yaml 46 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: Pull Request 4 | 5 | on: 6 | merge_group: 7 | pull_request: 8 | 9 | jobs: 10 | prepare: 11 | name: Prepare 12 | runs-on: ubuntu-latest 13 | outputs: 14 | changed-files: ${{ steps.changed-files.outputs.changed_files }} 15 | steps: 16 | - name: Get changed files 17 | id: changed-files 18 | uses: bjw-s-labs/action-changed-files@930cef8463348e168cab7235c47fe95a7a235f65 # v0.3.3 19 | with: 20 | path: apps 21 | include_only_directories: true 22 | max_depth: 1 23 | 24 | build: 25 | if: ${{ needs.prepare.outputs.changed-files != '[]' }} 26 | name: Build ${{ matrix.app }} 27 | needs: ["prepare"] 28 | uses: ./.github/workflows/app-builder.yaml 29 | permissions: 30 | contents: read 31 | packages: read 32 | secrets: inherit 33 | strategy: 34 | matrix: 35 | app: ${{ fromJSON(needs.prepare.outputs.changed-files) }} 36 | fail-fast: false 37 | max-parallel: 4 38 | with: 39 | app: ${{ matrix.app }} 40 | release: false 41 | 42 | status: 43 | if: ${{ !cancelled() }} 44 | name: Build Success 45 | needs: ["build"] 46 | runs-on: ubuntu-latest 47 | steps: 48 | - name: Any jobs failed? 49 | if: ${{ contains(needs.*.result, 'failure') }} 50 | run: exit 1 51 | 52 | - name: All jobs passed or skipped? 53 | if: ${{ !(contains(needs.*.result, 'failure')) }} 54 | run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" 55 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: Release 4 | 5 | on: 6 | push: 7 | branches: ["main"] 8 | paths: ["apps/**"] 9 | workflow_dispatch: 10 | inputs: 11 | app: 12 | type: string 13 | description: Application Name 14 | required: true 15 | release: 16 | type: boolean 17 | description: Release 18 | required: false 19 | default: false 20 | 21 | jobs: 22 | prepare: 23 | name: Prepare 24 | runs-on: ubuntu-latest 25 | outputs: 26 | changed-files: ${{ steps.changed-files.outputs.changed_files }} 27 | steps: 28 | - name: Get Changed Files 29 | id: changed-files 30 | uses: bjw-s-labs/action-changed-files@930cef8463348e168cab7235c47fe95a7a235f65 # v0.3.3 31 | with: 32 | path: apps 33 | include_only_directories: true 34 | max_depth: 1 35 | 36 | changed: 37 | if: ${{ needs.prepare.outputs.changed-files != '[]' || github.event_name == 'workflow_dispatch' }} 38 | name: Get Changed Apps 39 | needs: ["prepare"] 40 | runs-on: ubuntu-latest 41 | outputs: 42 | apps: ${{ steps.apps.outputs.apps }} 43 | steps: 44 | - name: Get Apps 45 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 46 | id: apps 47 | env: 48 | APPS: ${{ github.event_name == 'workflow_dispatch' && inputs.app || join(fromJSON(needs.prepare.outputs.changed-files), ' ') }} 49 | with: 50 | script: | 51 | const { APPS } = process.env; 52 | const appsToBuild = APPS.split(' ').filter(Boolean); 53 | core.setOutput('apps', JSON.stringify(appsToBuild)); 54 | console.log('apps:', JSON.stringify(appsToBuild, null, 2)); 55 | core.summary.addHeading('Apps to build:').addList(appsToBuild).write(); 56 | 57 | build: 58 | if: ${{ needs.changed.outputs.apps != '[]' }} 59 | name: Build ${{ matrix.app }} 60 | needs: ["changed"] 61 | uses: ./.github/workflows/app-builder.yaml 62 | permissions: 63 | attestations: write 64 | contents: write 65 | id-token: write 66 | packages: write 67 | security-events: write 68 | secrets: inherit 69 | strategy: 70 | matrix: 71 | app: ${{ fromJSON(needs.changed.outputs.apps) }} 72 | fail-fast: false 73 | max-parallel: 4 74 | with: 75 | app: ${{ matrix.app }} 76 | release: ${{ github.event_name == 'workflow_dispatch' && inputs.release || github.event_name == 'push' }} 77 | 78 | status: 79 | if: ${{ !cancelled() }} 80 | name: Build Success 81 | needs: ["build"] 82 | runs-on: ubuntu-latest 83 | steps: 84 | - name: Any jobs failed? 85 | if: ${{ contains(needs.*.result, 'failure') }} 86 | run: exit 1 87 | 88 | - name: All jobs passed or skipped? 89 | if: ${{ !(contains(needs.*.result, 'failure')) }} 90 | run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" 91 | -------------------------------------------------------------------------------- /.github/workflows/retry-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: Retry Release 4 | 5 | on: 6 | schedule: 7 | - cron: "30 1 * * *" 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | apps: 15 | name: Get App Inventory 16 | runs-on: ubuntu-latest 17 | outputs: 18 | apps: ${{ steps.inventory.outputs.apps }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 22 | with: 23 | persist-credentials: false 24 | 25 | - name: Get App Inventory 26 | uses: ./.github/actions/app-inventory 27 | id: inventory 28 | 29 | retry: 30 | if: ${{ needs.apps.outputs.apps != '[]' }} 31 | name: Retry Release 32 | runs-on: ubuntu-latest 33 | needs: ["apps"] 34 | strategy: 35 | matrix: 36 | app: ${{ fromJSON(needs.apps.outputs.apps) }} 37 | max-parallel: 4 38 | fail-fast: false 39 | steps: 40 | - name: Checkout 41 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 42 | with: 43 | persist-credentials: false 44 | 45 | - name: Generate Token 46 | uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 47 | id: app-token 48 | with: 49 | app-id: ${{ secrets.BOT_APP_ID }} 50 | private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} 51 | 52 | - name: Install Cosign 53 | uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2 54 | 55 | - name: Install regctl 56 | uses: regclient/actions/regctl-installer@4d6888fcc4842c9630f60ebc91715a45dd9bd7a3 # main 57 | 58 | - name: Get Bake Options 59 | id: app-options 60 | uses: ./.github/actions/app-options 61 | with: 62 | app: ${{ matrix.app }} 63 | 64 | - name: Get Container Registry Version 65 | id: registry 66 | env: 67 | GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} 68 | run: | 69 | if ! version=$(regctl image inspect ghcr.io/${{ github.repository_owner }}/${{ matrix.app }}:rolling \ 70 | | jq --raw-output '.config.Labels["org.opencontainers.image.version"]' 2>/dev/null) || [[ -z "${version}" ]]; 71 | then 72 | echo "Failed to get registry version for ${{ matrix.app }}" 73 | exit 1 74 | fi 75 | echo "version=${version}" >> $GITHUB_OUTPUT 76 | 77 | - if: ${{ steps.app-options.outputs.version != steps.registry.outputs.version }} 78 | name: Find Pull Request 79 | uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1.9.0 80 | id: find-pull-request 81 | with: 82 | github-token: ${{ steps.app-token.outputs.token }} 83 | labels: app/${{ matrix.app }} 84 | state: open 85 | 86 | - if: ${{ steps.find-pull-request.outputs.number != '' }} 87 | name: Retry Release 88 | env: 89 | GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} 90 | run: | # zizmor: ignore[template-injection] 91 | gh workflow run release.yaml \ 92 | --repo ${{ github.repository }} \ 93 | -f app=${{ matrix.app }} \ 94 | -f release=true 95 | 96 | { 97 | echo '## Retrying Release' 98 | echo 99 | echo '| Container Name | Actual Version | Expected Version |' 100 | echo '|----------------|------------------|----------------|' 101 | echo '| `${{ matrix.app }}` | `${{ steps.registry.outputs.version }}` | `${{ steps.app-options.outputs.version }}` |' 102 | } >> $GITHUB_STEP_SUMMARY 103 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: Stale 4 | 5 | on: 6 | schedule: 7 | - cron: "30 1 * * *" 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | stale: 15 | name: Stale 16 | runs-on: ubuntu-latest 17 | permissions: 18 | actions: write 19 | contents: write 20 | issues: write 21 | pull-requests: write 22 | steps: 23 | - name: Generate Token 24 | uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 25 | id: app-token 26 | with: 27 | app-id: "${{ secrets.BOT_APP_ID }}" 28 | private-key: "${{ secrets.BOT_APP_PRIVATE_KEY }}" 29 | 30 | - name: Stale 31 | uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0 32 | with: 33 | repo-token: ${{ steps.app-token.outputs.token }} 34 | stale-issue-message: | 35 | This issue has been marked as stale due to 60 days of inactivity. Please remove the stale label or leave a comment to keep it open. 36 | If no action is taken, it will be automatically closed in 7 days. 37 | stale-pr-message: | 38 | This pull request has been marked as stale due to 60 days of inactivity. Please remove the stale label or leave a comment to prevent it from being closed. 39 | If no action is taken, it will be automatically closed in 7 days. 40 | close-issue-message: | 41 | This issue has been automatically closed after 7 days of inactivity following the stale status. 42 | close-pr-message: | 43 | This pull request has been automatically closed after 7 days of inactivity following the stale status. 44 | stale-issue-label: stale 45 | stale-pr-label: stale 46 | delete-branch: true 47 | -------------------------------------------------------------------------------- /.github/workflows/test-version.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: Test Version 4 | 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | version: 9 | type: string 10 | description: Version 11 | required: true 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | test-version: 18 | name: Test Version (${{ inputs.version }}) 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 23 | with: 24 | persist-credentials: false 25 | 26 | - name: Get Application Versions 27 | uses: ./.github/actions/app-versions 28 | id: app-versions 29 | with: 30 | upstream-version: ${{ inputs.version }} 31 | 32 | - name: Check Version Results 33 | run: | 34 | echo "Input Version: ${{ inputs.version }}" 35 | echo "Is Valid Semver: ${{ steps.app-versions.outputs.is-valid-semver }}" 36 | echo "Semantic Version: ${{ steps.app-versions.outputs.semantic }}" 37 | echo "Raw Version: ${{ steps.app-versions.outputs.raw }}" 38 | -------------------------------------------------------------------------------- /.github/workflows/vulnerability-scan.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: Vulnerability Scan 4 | 5 | on: 6 | schedule: 7 | - cron: "30 1 * * *" 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | apps: 15 | name: Get App Inventory 16 | runs-on: ubuntu-latest 17 | outputs: 18 | apps: ${{ steps.inventory.outputs.apps }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 22 | with: 23 | persist-credentials: false 24 | 25 | - name: Get App Inventory 26 | uses: ./.github/actions/app-inventory 27 | id: inventory 28 | 29 | grype: 30 | name: Setup Grype 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Create Cache Key 34 | id: cache 35 | run: echo "key=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT 36 | 37 | - name: Restore Database 38 | id: database-restore 39 | uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 40 | with: 41 | key: grype-db-${{ steps.cache.outputs.key }} 42 | path: ~/.cache/grype/db 43 | 44 | - if: ${{ always() && steps.database-restore.outputs.cache-hit != 'true' }} 45 | name: Install Grype 46 | uses: anchore/scan-action/download-grype@2c901ab7378897c01b8efaa2d0c9bf519cc64b9e # v6.2.0 47 | id: grype 48 | 49 | - if: ${{ always() && steps.database-restore.outputs.cache-hit != 'true' }} 50 | name: Update Database 51 | run: | 52 | ${{ steps.grype.outputs.cmd }} db update 53 | 54 | - if: ${{ always() && steps.database-restore.outputs.cache-hit != 'true' }} 55 | name: Cache Database 56 | uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 57 | with: 58 | key: grype-db-${{ steps.cache.outputs.key }} 59 | path: ~/.cache/grype/db 60 | 61 | vulnerability-scan: 62 | name: Vulnerability Scan (${{ matrix.app }}) 63 | needs: ["apps", "grype"] 64 | runs-on: ubuntu-latest 65 | strategy: 66 | matrix: 67 | app: ${{ fromJSON(needs.apps.outputs.apps) }} 68 | max-parallel: 4 69 | fail-fast: false 70 | permissions: 71 | security-events: write 72 | contents: read 73 | steps: 74 | - name: Retreive Cache Key 75 | id: cache 76 | run: echo "key=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT 77 | 78 | - name: Restore Database 79 | uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 80 | with: 81 | key: grype-db-${{ steps.cache.outputs.key }} 82 | path: ~/.cache/grype/db 83 | 84 | - name: Scan 85 | uses: anchore/scan-action@2c901ab7378897c01b8efaa2d0c9bf519cc64b9e # v6.2.0 86 | id: scan 87 | with: 88 | fail-build: false 89 | image: ghcr.io/${{ github.repository_owner }}/${{ matrix.app }}:rolling 90 | severity-cutoff: high 91 | 92 | - name: Upload Report 93 | uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 94 | with: 95 | category: container:${{ matrix.app }} 96 | sarif_file: ${{ steps.scan.outputs.sarif }} 97 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bin 2 | .private 3 | docker-bake.json 4 | -------------------------------------------------------------------------------- /.mise.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | "aqua:cli/cli" = "2.74.0" 3 | "aqua:go-task/task" = "3.43.3" 4 | "aqua:GoogleContainerTools/container-structure-test" = "1.19.3" 5 | "aqua:jqlang/jq" = "1.7.1" 6 | "aqua:mikefarah/yq" = "4.45.4" 7 | 8 | [hooks] 9 | postinstall = [ 10 | "mkdir -p {{config_root}}/.bin/goss", 11 | "gh release download --repo goss-org/goss --skip-existing --pattern *-linux-amd64 --output {{config_root}}/.bin/goss/goss", 12 | "gh release download --repo goss-org/goss --skip-existing --pattern dgoss --output {{config_root}}/.bin/goss/dgoss", 13 | "chmod +x {{config_root}}/.bin/goss/{goss,dgoss}", 14 | ] 15 | -------------------------------------------------------------------------------- /.renovaterc.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: "https://docs.renovatebot.com/renovate-schema.json", 3 | extends: ["github>home-operations/renovate-config"], 4 | customManagers: [ 5 | { 6 | customType: "regex", 7 | description: "Process Annotations in Docker Bake", 8 | managerFilePatterns: ["/(^|/)docker-bake\\.hcl$/"], 9 | matchStrings: [ 10 | "datasource=(?\\S+) depName=(?\\S+)( versioning=(?\\S+))?\\n.+ = \"(?[^\"]+)\"", 11 | ], 12 | datasourceTemplate: "{{#if datasource}}{{{datasource}}}{{else}}github-releases{{/if}}", 13 | versioningTemplate: "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}", 14 | }, 15 | ], 16 | customDatasources: { 17 | qbittorrent: { 18 | defaultRegistryUrlTemplate: "https://github.com/userdocs/qbittorrent-nox-static/releases/latest/download/dependency-version.json", 19 | format: "json", 20 | transformTemplates: ['{"releases":[{"version": qbittorrent}]}'], 21 | }, 22 | "servarr-develop": { 23 | defaultRegistryUrlTemplate: "https://{{packageName}}.servarr.com/v1/update/develop/changes?os=linux&runtime=netcore", 24 | format: "json", 25 | transformTemplates: ['{"releases":[{"version": $[0].version}]}'], 26 | }, 27 | "servarr-nightly": { 28 | defaultRegistryUrlTemplate: "https://{{packageName}}.servarr.com/v1/update/nightly/changes?os=linux&runtime=netcore", 29 | format: "json", 30 | transformTemplates: ['{"releases":[{"version": $[0].version}]}'], 31 | }, 32 | "sonarr-develop": { 33 | defaultRegistryUrlTemplate: "https://services.sonarr.tv/v1/update/develop/changes?os=linux&runtime=netcore&version=4.0", 34 | format: "json", 35 | transformTemplates: ['{"releases":[{"version": $[0].version}]}'], 36 | }, 37 | plex: { 38 | defaultRegistryUrlTemplate: "https://plex.tv/api/downloads/5.json", 39 | format: "json", 40 | transformTemplates: ['{"releases":[{"version": computer.Linux.version}]}'], 41 | }, 42 | }, 43 | packageRules: [ 44 | { 45 | description: ["Release Rules for Application Updates"], 46 | addLabels: ["app/{{parentDir}}"], 47 | additionalBranchPrefix: "{{parentDir}}-", 48 | commitMessageExtra: "( {{currentVersion}} → {{newVersion}} )", 49 | commitMessageTopic: "{{depName}}", 50 | matchFileNames: ["**/docker-bake.hcl", "**/Dockerfile"], 51 | semanticCommitScope: "{{parentDir}}", 52 | semanticCommitType: "release", 53 | }, 54 | { 55 | description: ["Auto-merge Application Updates"], 56 | matchFileNames: ["**/docker-bake.hcl"], 57 | automerge: true, 58 | automergeType: "pr", 59 | ignoreTests: false, 60 | }, 61 | { 62 | description: ["Allowed Ubuntu Version for Base Images"], 63 | matchDatasources: ["docker"], 64 | matchPackageNames: ["/ubuntu/"], 65 | allowedVersions: "/24\\.04/", 66 | }, 67 | { 68 | description: ["Allowed Alpine Version for Base Images"], 69 | matchDatasources: ["docker"], 70 | matchFileNames: ["**/Dockerfile"], 71 | matchPackageNames: ["/alpine/"], 72 | allowedVersions: "/3\\.22/", 73 | }, 74 | { 75 | description: ["Allowed Python Version for Base Images"], 76 | matchDatasources: ["docker"], 77 | matchFileNames: ["**/Dockerfile"], 78 | matchPackageNames: ["/python/"], 79 | allowedVersions: "/3\\.13-alpine3\\.22/", 80 | }, 81 | { 82 | description: ["Allowed Java Version for NZBHydra2 Base Image"], 83 | matchDatasources: ["docker"], 84 | matchFileNames: ["apps/nzbhydra2/Dockerfile"], 85 | matchPackageNames: ["/amazoncorretto/"], 86 | allowedVersions: "/17-alpine3\\.21/", 87 | }, 88 | ], 89 | } 90 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | disable=SC1091 2 | disable=SC2086 3 | disable=SC2090 4 | disable=SC2148 5 | disable=SC2155 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.json5": "json5" 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2025 Home Operations 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ## Containers 4 | 5 | _An opinionated collection of container images_ 6 | 7 |
8 | 9 |
10 | 11 | ![GitHub Repo stars](https://img.shields.io/github/stars/home-operations/containers?style=for-the-badge) 12 | ![GitHub forks](https://img.shields.io/github/forks/home-operations/containers?style=for-the-badge) 13 | ![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/home-operations/containers/release.yaml?style=for-the-badge&label=Release) 14 | 15 |
16 | 17 | Welcome to our container images! If you are looking for a container, start by [browsing the GitHub Packages page for this repository's packages](https://github.com/orgs/home-operations/packages?repo_name=containers). 18 | 19 | ## Mission Statement 20 | 21 | Our goal is to provide [semantically versioned](https://semver.org/), [rootless](https://rootlesscontaine.rs/), and [multi-architecture](https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/) containers for various applications. 22 | 23 | We adhere to the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle), logging to stdout, maintaining [one process per container](https://testdriven.io/tips/59de3279-4a2d-4556-9cd0-b444249ed31e/), avoiding tools like [s6-overlay](https://github.com/just-containers/s6-overlay), and building all images on top of [Alpine](https://hub.docker.com/_/alpine) or [Ubuntu](https://hub.docker.com/_/ubuntu). 24 | 25 | ## Features 26 | 27 | ### Tag Immutability 28 | 29 | Containers built here do not use immutable tags in the traditional sense, as seen with [linuxserver.io](https://fleet.linuxserver.io/) or [Bitnami](https://bitnami.com/stacks/containers). Instead, we insist on pinning to the `sha256` digest of the image. While this approach is less visually appealing, it ensures functionality and immutability. 30 | 31 | | Container | Immutable | 32 | |-----------------------|----| 33 | | `ghcr.io/home-operations/home-assistant:rolling` | ❌ | 34 | | `ghcr.io/home-operations/home-assistant:2025.5.1` | ❌ | 35 | | `ghcr.io/home-operations/home-assistant:rolling@sha256:8053...` | ✅ | 36 | | `ghcr.io/home-operations/home-assistant:2025.5.1@sha256:8053...` | ✅ | 37 | 38 | _If pinning an image to the `sha256` digest, tools like [Renovate](https://github.com/renovatebot/renovate) can update containers based on digest or version changes._ 39 | 40 | ### Rootless 41 | 42 | By default the majority of our containers run as a non-root user (`65534:65534`), you are able to change the user/group by updating your configuration files. 43 | 44 | #### Docker Compose 45 | 46 | ```yaml 47 | services: 48 | home-assistant: 49 | image: ghcr.io/home-operations/home-assistant:2025.5.1 50 | container_name: home-assistant 51 | user: 1000:1000 # The data volume permissions must match this user:group 52 | read_only: true # May require mounting in additional dirs as tmpfs 53 | tmpfs: 54 | - /tmp:rw 55 | # ... 56 | ``` 57 | 58 | #### Kubernetes 59 | 60 | ```yaml 61 | apiVersion: apps/v1 62 | kind: Deployment 63 | metadata: 64 | name: home-assistant 65 | # ... 66 | spec: 67 | # ... 68 | template: 69 | # ... 70 | spec: 71 | containers: 72 | - name: home-assistant 73 | image: ghcr.io/home-operations/home-assistant:2025.5.1 74 | securityContext: # May require mounting in additional dirs as emptyDir 75 | allowPrivilegeEscalation: false 76 | capabilities: 77 | drop: 78 | - ALL 79 | readOnlyRootFilesystem: true 80 | volumeMounts: 81 | - name: tmp 82 | mountPath: /tmp 83 | # ... 84 | securityContext: 85 | runAsUser: 1000 86 | runAsGroup: 1000 87 | fsGroup: 65534 # (Requires CSI support) 88 | fsGroupChangePolicy: OnRootMismatch # (Requires CSI support) 89 | volumes: 90 | - name: tmp 91 | emptyDir: {} 92 | # ... 93 | # ... 94 | ``` 95 | 96 | ### Passing Arguments to Applications 97 | 98 | Some applications only allow certain configurations via command-line arguments rather than environment variables. For such cases, refer to the Kubernetes documentation on [defining commands and arguments for a container](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/). Then, specify the desired arguments as shown below: 99 | 100 | ```yaml 101 | args: 102 | - --port 103 | - "8080" 104 | ``` 105 | 106 | ### Configuration Volume 107 | 108 | For applications requiring persistent configuration data, the configuration volume is hardcoded to `/config` within the container. In most cases, this path cannot be changed. 109 | 110 | ### Verify Image Signature 111 | 112 | These container images are signed using the [attest-build-provenance](https://github.com/actions/attest-build-provenance) action. 113 | 114 | To verify that the image was built by GitHub CI, use the following command: 115 | 116 | ```sh 117 | gh attestation verify --repo home-operations/containers oci://ghcr.io/home-operations/${APP}:${TAG} 118 | ``` 119 | 120 | or by using [cosign](https://github.com/sigstore/cosign): 121 | 122 | ```sh 123 | cosign verify-attestation --new-bundle-format --type slsaprovenance1 \ 124 | --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ 125 | --certificate-identity-regexp "^https://github.com/home-operations/containers/.github/workflows/app-builder.yaml@refs/heads/main" \ 126 | ghcr.io/home-operations/${APP}:${TAG} 127 | ``` 128 | 129 | ### Eschewed Features 130 | 131 | This repository does not support multiple "channels" for the same application. For example: 132 | 133 | - **Prowlarr**, **Radarr**, **Lidarr**, and **Sonarr** only publish the **develop** branch, not the **master** (stable) branch. 134 | - **qBittorrent** is only published with **LibTorrent 2.x**. 135 | 136 | This approach ensures consistency and focuses on streamlined builds. 137 | 138 | ## Contributing 139 | 140 | We encourage the use of official upstream container images whenever possible. However, contributing to this repository might make sense if: 141 | 142 | - The upstream application is **actively maintained**. 143 | - **And** one of the following applies: 144 | - No official upstream container exists. 145 | - The official image does not support **multi-architecture builds**. 146 | - The official image uses tools like **s6-overlay**, **gosu**, or other unconventional initialization mechanisms. 147 | 148 | ## Deprecations 149 | 150 | Containers in this repository may be deprecated for the following reasons: 151 | 152 | 1. The upstream application is **no longer actively maintained**. 153 | 2. An **official upstream container exists** that aligns with this repository's mission statement. 154 | 3. The **maintenance burden** is unsustainable, such as frequent build failures or instability. 155 | 156 | **Note**: Deprecated containers will be announced with a release and remain available in the registry for 6 months before removal. 157 | 158 | ## Maintaining a Fork 159 | 160 | Forking this repository is straightforward. Keep the following in mind: 161 | 162 | 1. **Renovate Bot**: Set up a GitHub Bot for Renovate by following the instructions [here](https://github.com/renovatebot/github-action). 163 | 2. **Renovate Configuration**: Configuration files are located in the [`.github`](https://github.com/home-operations/.github) and [renovate-config](https://github.com/home-operations/renovate-config) repositories. 164 | 3. **Lowercase Naming**: Ensure your GitHub username/organization and repository names are entirely lowercase to comply with GHCR requirements. Rename them or update workflows as needed. 165 | 166 | ## Credits 167 | 168 | This repository draws inspiration and ideas from the home-ops community, [hotio.dev](https://hotio.dev/), and [linuxserver.io](https://www.linuxserver.io/) contributors. 169 | -------------------------------------------------------------------------------- /Taskfile.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 3 | version: '3' 4 | 5 | set: [pipefail] 6 | shopt: [globstar] 7 | 8 | vars: 9 | BIN_DIR: '{{.ROOT_DIR}}/.bin' 10 | 11 | tasks: 12 | 13 | default: 14 | cmd: task --list 15 | silent: true 16 | 17 | local-build-*: 18 | desc: Build and Test an app via Docker Buildx 19 | dir: '{{.WORK_DIR}}' 20 | cmds: 21 | - rsync -a --ignore-existing --progress {{.ROOT_DIR}}/include/ {{.WORK_DIR}} 22 | - rsync -a --ignore-existing --progress {{.ROOT_DIR}}/apps/{{.APP}}/ {{.WORK_DIR}} 23 | - docker buildx bake --no-cache --metadata-file docker-bake.json --load 24 | - | 25 | IMAGE=$(jq --raw-output '."image-local"."image.name"' docker-bake.json) 26 | case "{{.TEST_TOOL}}" in 27 | "cst") 28 | container-structure-test test --image "${IMAGE}" --config tests.yaml 29 | ;; 30 | "goss") 31 | {{.BIN_DIR}}/goss/dgoss run "${IMAGE}" 32 | ;; 33 | esac 34 | echo "To run this image, use the following command:" 35 | echo "docker run --rm -it ${IMAGE}" 36 | - defer: rm -rf {{.WORK_DIR}} 37 | env: 38 | GOSS_FILE: tests.yaml 39 | GOSS_OPTS: --retry-timeout 60s --sleep 1s 40 | GOSS_PATH: '{{.BIN_DIR}}/goss/goss' 41 | vars: 42 | APP: '{{index .MATCH 0}}' 43 | TEST_TOOL: 44 | sh: yq --exit-status '.schemaVersion' {{.ROOT_DIR}}/apps/{{.APP}}/tests.yaml &>/dev/null && echo "cst" || echo "goss" 45 | WORK_DIR: 46 | sh: mktemp -d 47 | preconditions: 48 | - test -f {{.BIN_DIR}}/goss/dgoss 49 | - test -f {{.BIN_DIR}}/goss/goss 50 | - test -f {{.ROOT_DIR}}/apps/{{.APP}}/docker-bake.hcl 51 | - test -f {{.ROOT_DIR}}/apps/{{.APP}}/tests.yaml 52 | - test -f {{.ROOT_DIR}}/apps/{{.APP}}/Dockerfile 53 | - which container-structure-test docker gh jq yq 54 | 55 | remote-build-*: 56 | desc: Build and Test an app via GitHub Actions 57 | cmds: 58 | - gh workflow run release.yaml -f app={{.APP}} -f release={{.RELEASE}} 59 | vars: 60 | APP: '{{index .MATCH 0}}' 61 | RELEASE: '{{.RELEASE | default "false"}}' 62 | preconditions: 63 | - gh auth status 64 | - test -f {{.ROOT_DIR}}/.github/workflows/release.yaml 65 | - which gh 66 | 67 | generate-label-config: 68 | desc: Generate labels and labeler config 69 | cmds: 70 | - for: { var: apps } 71 | cmd: | 72 | yq --inplace '. += [{"name": "app/{{.ITEM}}", "color": "027fa0"}]' {{.ROOT_DIR}}/.github/labels.yaml 73 | - for: { var: apps } 74 | cmd: | 75 | yq --inplace '. += {"app/{{.ITEM}}": [{"changed-files": [{"any-glob-to-any-file": ["apps/{{.ITEM}}/**"]}]}]}' {{.ROOT_DIR}}/.github/labeler.yaml 76 | vars: 77 | apps: 78 | sh: ls --directory {{.ROOT_DIR}}/apps/*/ | xargs --max-args=1 basename 79 | preconditions: 80 | - sh: '[[ -z {{.CLI_FORCE}} ]]' 81 | -------------------------------------------------------------------------------- /apps/actions-runner/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | ARG VERSION 4 | FROM ghcr.io/actions/actions-runner:${VERSION} 5 | ARG TARGETARCH 6 | 7 | ENV HOMEBREW_NO_ANALYTICS=1 \ 8 | HOMEBREW_NO_ENV_HINTS=1 \ 9 | HOMEBREW_NO_INSTALL_CLEANUP=1 10 | 11 | USER root 12 | 13 | RUN \ 14 | apt-get -qq update \ 15 | && \ 16 | apt-get -qq install -y --no-install-recommends --no-install-suggests \ 17 | ca-certificates \ 18 | jo \ 19 | moreutils \ 20 | wget \ 21 | zstd \ 22 | && \ 23 | case "${TARGETARCH}" in \ 24 | 'amd64') apt-get -qq install -y --no-install-recommends --no-install-suggests gcc ;; \ 25 | esac \ 26 | && \ 27 | curl -fsSL "https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${TARGETARCH}" -o /usr/local/bin/yq \ 28 | && chmod +x /usr/local/bin/yq \ 29 | && \ 30 | mkdir -p -m 755 /etc/apt/keyrings \ 31 | && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \ 32 | && cat $out | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \ 33 | && chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ 34 | && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ 35 | && apt update \ 36 | && apt install gh -y \ 37 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 38 | 39 | USER runner 40 | 41 | RUN \ 42 | case "${TARGETARCH}" in \ 43 | 'amd64') /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" ;; \ 44 | esac 45 | -------------------------------------------------------------------------------- /apps/actions-runner/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "actions-runner" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=docker depName=ghcr.io/actions/actions-runner 9 | default = "2.325.0" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/actions/runner" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/actions-runner/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | schemaVersion: "2.0.0" 3 | fileExistenceTests: 4 | - name: yq 5 | path: /usr/local/bin/yq 6 | shouldExist: true 7 | -------------------------------------------------------------------------------- /apps/bazarr/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/python:3.13-alpine3.22 4 | ARG VENDOR 5 | ARG VERSION 6 | 7 | ENV \ 8 | CRYPTOGRAPHY_DONT_BUILD_RUST=1 \ 9 | PIP_BREAK_SYSTEM_PACKAGES=1 \ 10 | PIP_DISABLE_PIP_VERSION_CHECK=1 \ 11 | PIP_NO_CACHE_DIR=1 \ 12 | PIP_ROOT_USER_ACTION=ignore \ 13 | PYTHONDONTWRITEBYTECODE=1 \ 14 | PYTHONUNBUFFERED=1 \ 15 | UV_NO_CACHE=true \ 16 | UV_SYSTEM_PYTHON=true \ 17 | UV_EXTRA_INDEX_URL="https://wheel-index.linuxserver.io/alpine-3.22/" 18 | 19 | ENV \ 20 | BAZARR__PORT=6767 \ 21 | BAZARR_PACKAGE_AUTHOR=${VENDOR} \ 22 | BAZARR_PACKAGE_VERSION=${VERSION} \ 23 | BAZARR_VERSION=${VERSION} 24 | 25 | USER root 26 | WORKDIR /app 27 | 28 | RUN \ 29 | apk add --no-cache \ 30 | bash \ 31 | ca-certificates \ 32 | catatonit \ 33 | coreutils \ 34 | curl \ 35 | ffmpeg \ 36 | jq \ 37 | libxml2 \ 38 | libpq \ 39 | libxslt \ 40 | mediainfo \ 41 | nano \ 42 | trurl \ 43 | tzdata \ 44 | unzip \ 45 | && \ 46 | apk add --no-cache --virtual .build-deps \ 47 | build-base \ 48 | cargo \ 49 | libffi-dev \ 50 | libpq-dev \ 51 | libxml2-dev \ 52 | libxslt-dev \ 53 | && \ 54 | curl -fsSL -o /tmp/app.zip "https://github.com/morpheus65535/bazarr/releases/download/${VERSION}/bazarr.zip" \ 55 | && unzip -q /tmp/app.zip -d /app/bin \ 56 | && sed -i '/Pillow>/s/ --only-binary=Pillow//; /Pillow>/a --only-binary=Pillow' /app/bin/requirements.txt \ 57 | && pip install uv \ 58 | && uv pip install \ 59 | --requirement /app/bin/requirements.txt \ 60 | --requirement /app/bin/postgres-requirements.txt \ 61 | && chown -R root:root /app && chmod -R 755 /app \ 62 | && pip uninstall --yes uv && apk del --purge .build-deps \ 63 | && rm -rf /root/.cache /root/.cargo /tmp/* /app/bin/bin 64 | 65 | COPY . / 66 | 67 | COPY --from=ghcr.io/linuxserver/unrar:latest /usr/bin/unrar-alpine /usr/bin/unrar 68 | 69 | USER nobody:nogroup 70 | WORKDIR /config 71 | VOLUME ["/config"] 72 | 73 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 74 | -------------------------------------------------------------------------------- /apps/bazarr/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "bazarr" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=morpheus65535/bazarr 9 | default = "v1.5.2" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/morpheus65535/bazarr" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/bazarr/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec \ 4 | /usr/local/bin/python \ 5 | /app/bin/bazarr.py \ 6 | --no-update True \ 7 | --config /config \ 8 | --port ${BAZARR__PORT} \ 9 | "$@" 10 | -------------------------------------------------------------------------------- /apps/bazarr/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | python: 5 | running: true 6 | port: 7 | tcp:6767: 8 | listening: true 9 | http: 10 | http://localhost:6767: 11 | status: 200 12 | file: 13 | /usr/local/bin/python: 14 | exists: true 15 | /usr/bin/unrar: 16 | exists: true 17 | -------------------------------------------------------------------------------- /apps/beets/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/node:22-alpine AS betanin 4 | WORKDIR /src 5 | ADD https://github.com/sentriz/betanin.git . 6 | WORKDIR /src/betanin_client 7 | ENV PRODUCTION=true 8 | ENV NODE_OPTIONS=--openssl-legacy-provider 9 | RUN npm install && npm run-script build 10 | 11 | FROM docker.io/library/python:3.13-alpine3.22 12 | ARG VERSION 13 | 14 | WORKDIR /src 15 | 16 | ENV \ 17 | CRYPTOGRAPHY_DONT_BUILD_RUST=1 \ 18 | PIP_BREAK_SYSTEM_PACKAGES=1 \ 19 | PIP_DISABLE_PIP_VERSION_CHECK=1 \ 20 | PIP_NO_CACHE_DIR=1 \ 21 | PIP_ROOT_USER_ACTION=ignore \ 22 | PYTHONDONTWRITEBYTECODE=1 \ 23 | PYTHONUNBUFFERED=1 \ 24 | UV_NO_CACHE=true \ 25 | UV_SYSTEM_PYTHON=true \ 26 | UV_EXTRA_INDEX_URL="https://wheel-index.linuxserver.io/alpine-3.22/" 27 | 28 | ENV HOME=/config \ 29 | BETANIN_HOST=0.0.0.0 \ 30 | BETANIN_PORT=8080 31 | 32 | RUN \ 33 | apk add --no-cache \ 34 | bash \ 35 | catatonit \ 36 | chromaprint \ 37 | curl \ 38 | expat \ 39 | ffmpeg \ 40 | fftw \ 41 | flac \ 42 | gdbm \ 43 | gobject-introspection \ 44 | gst-plugins-good \ 45 | gstreamer \ 46 | imagemagick \ 47 | jpeg \ 48 | jq \ 49 | lame \ 50 | libffi \ 51 | libpng \ 52 | mpg123 \ 53 | openjpeg \ 54 | sqlite-libs \ 55 | tzdata \ 56 | && \ 57 | apk add --no-cache --virtual=.build-deps \ 58 | git \ 59 | && \ 60 | apk add --no-cache --repository="https://dl-cdn.alpinelinux.org/alpine/edge/community/" \ 61 | mp3gain \ 62 | && \ 63 | apk add --no-cache --repository="https://dl-cdn.alpinelinux.org/alpine/edge/testing/" \ 64 | mp3val \ 65 | && \ 66 | pip install uv \ 67 | && \ 68 | uv pip install \ 69 | https://github.com/beetbox/beets/releases/download/${VERSION}/beets-${VERSION#*v}-py3-none-any.whl \ 70 | git+https://github.com/sentriz/betanin.git \ 71 | beetcamp \ 72 | beets-extrafiles \ 73 | beets-lidarr-fields \ 74 | beets-noimport \ 75 | pyacoustid \ 76 | pylast \ 77 | python3-discogs-client \ 78 | && pip uninstall --yes uv \ 79 | && apk del --purge .build-deps \ 80 | && rm -rf /tmp/* 81 | 82 | COPY --from=betanin /src/betanin_client/dist/ /usr/local/lib/python3.13/site-packages/betanin_client/dist/ 83 | COPY . / 84 | 85 | USER nobody:nogroup 86 | WORKDIR /config 87 | VOLUME ["/config"] 88 | 89 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 90 | -------------------------------------------------------------------------------- /apps/beets/defaults/config-beets.yaml: -------------------------------------------------------------------------------- 1 | library: /config/beets.db 2 | directory: /Music 3 | 4 | import: 5 | write: yes 6 | copy: yes 7 | move: no 8 | link: no 9 | hardlink: no 10 | reflink: no 11 | delete: no 12 | resume: ask 13 | incremental: no 14 | incremental_skip_later: no 15 | from_scratch: no 16 | quiet_fallback: skip 17 | none_rec_action: ask 18 | timid: no 19 | log: 20 | autotag: yes 21 | quiet: no 22 | singletons: no 23 | default_action: apply 24 | languages: [] 25 | detail: no 26 | flat: no 27 | group_albums: no 28 | pretend: false 29 | search_ids: [] 30 | duplicate_action: ask 31 | bell: no 32 | set_fields: {} 33 | 34 | clutter: ["Thumbs.DB", ".DS_Store"] 35 | ignore: [".*", "*~", "System Volume Information", "lost+found"] 36 | ignore_hidden: yes 37 | 38 | replace: 39 | '[\\/]': _ 40 | '^\.': _ 41 | '[\x00-\x1f]': _ 42 | '[<>:"\?\*\|]': _ 43 | '\.$': _ 44 | '\s+$': '' 45 | '^\s+': '' 46 | '^-': _ 47 | path_sep_replace: _ 48 | drive_sep_replace: _ 49 | asciify_paths: false 50 | art_filename: cover 51 | max_filename_length: 0 52 | 53 | aunique: 54 | keys: albumartist album 55 | disambiguators: albumtype year label catalognum albumdisambig releasegroupdisambig 56 | bracket: '[]' 57 | 58 | overwrite_null: 59 | album: [] 60 | track: [] 61 | 62 | plugins: [] 63 | pluginpath: [] 64 | threaded: yes 65 | timeout: 5.0 66 | per_disc_numbering: no 67 | verbose: 0 68 | terminal_encoding: 69 | original_date: no 70 | artist_credit: no 71 | id3v23: no 72 | va_name: "Various Artists" 73 | 74 | ui: 75 | terminal_width: 80 76 | length_diff_thresh: 10.0 77 | color: yes 78 | colors: 79 | text_success: green 80 | text_warning: yellow 81 | text_error: red 82 | text_highlight: red 83 | text_highlight_minor: lightgray 84 | action_default: turquoise 85 | action: blue 86 | 87 | format_item: $artist - $album - $title 88 | format_album: $albumartist - $album 89 | time_format: '%Y-%m-%d %H:%M:%S' 90 | format_raw_length: no 91 | 92 | sort_album: albumartist+ album+ 93 | sort_item: artist+ album+ disc+ track+ 94 | sort_case_insensitive: yes 95 | 96 | paths: 97 | default: $albumartist/$album%aunique{}/$track $title 98 | singleton: Non-Album/$artist/$title 99 | comp: Compilations/$album%aunique{}/$track $title 100 | 101 | statefile: state.pickle 102 | 103 | musicbrainz: 104 | host: musicbrainz.org 105 | https: no 106 | ratelimit: 1 107 | ratelimit_interval: 1.0 108 | searchlimit: 5 109 | extra_tags: [] 110 | genres: no 111 | 112 | match: 113 | strong_rec_thresh: 0.04 114 | medium_rec_thresh: 0.25 115 | rec_gap_thresh: 0.25 116 | max_rec: 117 | missing_tracks: medium 118 | unmatched_tracks: medium 119 | distance_weights: 120 | source: 2.0 121 | artist: 3.0 122 | album: 3.0 123 | media: 1.0 124 | mediums: 1.0 125 | year: 1.0 126 | country: 0.5 127 | label: 0.5 128 | catalognum: 0.5 129 | albumdisambig: 0.5 130 | album_id: 5.0 131 | tracks: 2.0 132 | missing_tracks: 0.9 133 | unmatched_tracks: 0.6 134 | track_title: 3.0 135 | track_artist: 2.0 136 | track_index: 1.0 137 | track_length: 2.0 138 | track_id: 5.0 139 | preferred: 140 | countries: [] 141 | media: [] 142 | original_year: no 143 | ignored: [] 144 | required: [] 145 | ignored_media: [] 146 | ignore_data_tracks: yes 147 | ignore_video_tracks: yes 148 | track_length_grace: 10 149 | track_length_max: 30 150 | -------------------------------------------------------------------------------- /apps/beets/defaults/config-betanin.toml: -------------------------------------------------------------------------------- 1 | [frontend] 2 | username = "beets" 3 | password = "" 4 | [clients] 5 | api_key = "" 6 | [server] 7 | num_parallel_jobs = 1 8 | [notifications.services] 9 | [notifications.strings] 10 | title = "Music Imported" 11 | body = "$name" 12 | -------------------------------------------------------------------------------- /apps/beets/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "beets" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=beetbox/beets 9 | default = "v2.3.1" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/beetbox/beets" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/beets/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export BEETSDIR="/config/.config/beets" 4 | export BETANINDIR="/config/.config/betanin" 5 | 6 | if [[ ! -f "${BEETSDIR}/config.yaml" ]]; then 7 | mkdir -p "${BEETSDIR}" 8 | cp /defaults/config-beets.yaml "${BEETSDIR}/config.yaml" 9 | fi 10 | 11 | if [[ ! -f "${BETANINDIR}/config.toml" ]]; then 12 | mkdir -p "${BETANINDIR}" 13 | cp /defaults/config-betanin.toml "${BETANINDIR}/config.toml" 14 | 15 | password=$(tr -dc 'a-z0-9' < /dev/urandom | fold -w 32 | head -n 1) 16 | api_key=$(tr -dc 'a-z0-9' < /dev/urandom | fold -w 32 | head -n 1) 17 | sed -i -e "s/^password *=.*$/password = \"${password}\"/g" "${BETANINDIR}/config.toml" 18 | sed -i -e "s/^api_key *=.*$/api_key = \"${api_key}\"/g" "${BETANINDIR}/config.toml" 19 | echo "Created password and api keys for the user beets:" 20 | echo "Password: ${password}" 21 | echo "API Key: ${api_key}" 22 | fi 23 | 24 | [[ -n "${BETANIN__PASSWORD}" ]] && sed -i -e "s/^password *=.*$/password = \"${BETANIN__PASSWORD}\"/g" "${BETANINDIR}/config.toml" 25 | [[ -n "${BETANIN__API_KEY}" ]] && sed -i -e "s/^api_key *=.*$/api_key = \"${BETANIN__API_KEY}\"/g" "${BETANINDIR}/config.toml" 26 | 27 | exec \ 28 | /usr/local/bin/betanin \ 29 | "$@" 30 | -------------------------------------------------------------------------------- /apps/beets/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | betanin: 5 | running: true 6 | port: 7 | tcp:8080: 8 | listening: true 9 | http: 10 | http://localhost:8080: 11 | status: 200 12 | -------------------------------------------------------------------------------- /apps/busybox/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | ARG VERSION 4 | FROM docker.io/library/busybox:${VERSION} 5 | -------------------------------------------------------------------------------- /apps/busybox/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "busybox" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=docker depName=docker.io/library/busybox 9 | default = "1.37.0" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://www.busybox.net" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/busybox/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | schemaVersion: "2.0.0" 3 | fileExistenceTests: 4 | - name: sh 5 | path: /bin/sh 6 | shouldExist: true 7 | -------------------------------------------------------------------------------- /apps/cni-plugins/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG TARGETARCH 5 | ARG VERSION 6 | 7 | ENV CNI_BIN_DIR=/host/opt/cni/bin 8 | 9 | USER root 10 | 11 | RUN \ 12 | apk add --no-cache \ 13 | rsync \ 14 | && \ 15 | apk add --no-cache --virtual=.build-deps \ 16 | curl \ 17 | tar \ 18 | && mkdir -p /plugins \ 19 | && curl -fsSL "https://github.com/containernetworking/plugins/releases/download/${VERSION}/cni-plugins-linux-${TARGETARCH}-${VERSION}.tgz" \ 20 | | tar xzf - -C /plugins --strip-components=1 \ 21 | && apk del --purge .build-deps \ 22 | && rm -rf /tmp/* 23 | 24 | CMD rsync -av /plugins/* $CNI_BIN_DIR 25 | -------------------------------------------------------------------------------- /apps/cni-plugins/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "cni-plugins" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=containernetworking/plugins 9 | default = "v1.7.1" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/containernetworking/plugins" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/cni-plugins/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | schemaVersion: "2.0.0" 3 | fileExistenceTests: 4 | - name: macvlan 5 | path: /plugins/macvlan 6 | shouldExist: true 7 | -------------------------------------------------------------------------------- /apps/deluge/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG VERSION 5 | 6 | ENV DELUGE_BIN="deluged" \ 7 | XDG_CONFIG_HOME="/config" \ 8 | PYTHON_EGG_CACHE="/config/plugins/.python-eggs" \ 9 | TMPDIR="/tmp" 10 | 11 | USER root 12 | 13 | RUN \ 14 | apk add --no-cache \ 15 | 7zip \ 16 | bash \ 17 | ca-certificates \ 18 | catatonit \ 19 | coreutils \ 20 | curl \ 21 | jq \ 22 | nano \ 23 | py3-future \ 24 | py3-requests \ 25 | tzdata \ 26 | && \ 27 | apk add --no-cache --repository="https://dl-cdn.alpinelinux.org/alpine/edge/community" \ 28 | deluge=="${VERSION}" \ 29 | && \ 30 | apk add --no-cache --repository="https://dl-cdn.alpinelinux.org/alpine/edge/testing" \ 31 | py3-geoip \ 32 | && \ 33 | mkdir -p /usr/share/GeoIP \ 34 | && \ 35 | curl -fsSL "https://mailfud.org/geoip-legacy/GeoIP.dat.gz" \ 36 | | gunzip > /usr/share/GeoIP/GeoIP.dat \ 37 | && rm -rf /tmp/* 38 | 39 | COPY . / 40 | 41 | COPY --from=ghcr.io/linuxserver/unrar:latest /usr/bin/unrar-alpine /usr/bin/unrar 42 | 43 | USER nobody:nogroup 44 | WORKDIR /config 45 | VOLUME ["/config"] 46 | 47 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 48 | -------------------------------------------------------------------------------- /apps/deluge/defaults/core.conf: -------------------------------------------------------------------------------- 1 | { 2 | "file": 1, 3 | "format": 1 4 | }{ 5 | "add_paused": false, 6 | "allow_remote": false, 7 | "auto_manage_prefer_seeds": false, 8 | "auto_managed": true, 9 | "cache_expiry": 60, 10 | "cache_size": 512, 11 | "copy_torrent_file": false, 12 | "daemon_port": 58846, 13 | "del_copy_torrent_file": false, 14 | "dht": true, 15 | "dont_count_slow_torrents": false, 16 | "download_location": "/downloads", 17 | "download_location_paths_list": [], 18 | "enabled_plugins": [], 19 | "enc_in_policy": 1, 20 | "enc_level": 2, 21 | "enc_out_policy": 1, 22 | "geoip_db_location": "/usr/share/GeoIP/GeoIP.dat", 23 | "ignore_limits_on_local_network": true, 24 | "info_sent": 0.0, 25 | "listen_interface": "", 26 | "listen_ports": [ 27 | 6881, 28 | 6891 29 | ], 30 | "listen_random_port": 51765, 31 | "listen_reuse_port": true, 32 | "listen_use_sys_port": false, 33 | "lsd": true, 34 | "max_active_downloading": 3, 35 | "max_active_limit": 8, 36 | "max_active_seeding": 5, 37 | "max_connections_global": 200, 38 | "max_connections_per_second": 20, 39 | "max_connections_per_torrent": -1, 40 | "max_download_speed": -1.0, 41 | "max_download_speed_per_torrent": -1, 42 | "max_half_open_connections": 50, 43 | "max_upload_slots_global": 4, 44 | "max_upload_slots_per_torrent": -1, 45 | "max_upload_speed": -1.0, 46 | "max_upload_speed_per_torrent": -1, 47 | "move_completed": false, 48 | "move_completed_path": "/downloads", 49 | "move_completed_paths_list": [], 50 | "natpmp": true, 51 | "new_release_check": true, 52 | "outgoing_interface": "", 53 | "outgoing_ports": [ 54 | 0, 55 | 0 56 | ], 57 | "path_chooser_accelerator_string": "Tab", 58 | "path_chooser_auto_complete_enabled": true, 59 | "path_chooser_max_popup_rows": 20, 60 | "path_chooser_show_chooser_button_on_localhost": true, 61 | "path_chooser_show_hidden_files": false, 62 | "peer_tos": "0x00", 63 | "plugins_location": "/config/plugins", 64 | "pre_allocate_storage": false, 65 | "prioritize_first_last_pieces": false, 66 | "proxy": { 67 | "anonymous_mode": false, 68 | "force_proxy": false, 69 | "hostname": "", 70 | "password": "", 71 | "port": 8080, 72 | "proxy_hostnames": true, 73 | "proxy_peer_connections": true, 74 | "proxy_tracker_connections": true, 75 | "type": 0, 76 | "username": "" 77 | }, 78 | "queue_new_to_top": false, 79 | "random_outgoing_ports": true, 80 | "random_port": true, 81 | "rate_limit_ip_overhead": true, 82 | "remove_seed_at_ratio": false, 83 | "seed_time_limit": 180, 84 | "seed_time_ratio_limit": 7.0, 85 | "send_info": false, 86 | "sequential_download": false, 87 | "share_ratio_limit": 2.0, 88 | "shared": false, 89 | "stop_seed_at_ratio": false, 90 | "stop_seed_ratio": 2.0, 91 | "super_seeding": false, 92 | "torrentfiles_location": "/config/torrents", 93 | "upnp": true, 94 | "utpex": true 95 | } 96 | -------------------------------------------------------------------------------- /apps/deluge/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "deluge" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=repology depName=alpine_edge/deluge 9 | default = "2.2.0-r0" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/deluge-torrent/deluge" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/deluge/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ ! -f /config/core.conf ]]; then 4 | cp /defaults/core.conf /config/core.conf 5 | fi 6 | 7 | mkdir -p /config/plugins/.python-eggs 8 | 9 | DELUGE_OPTS=( 10 | "--do-not-daemonize" 11 | "--config" "/config" 12 | ) 13 | 14 | if [[ ${DELUGE_BIN} == "deluged" ]]; then 15 | DELUGE_OPTS+=("--loglevel" "info") 16 | elif [[ ${DELUGE_BIN} == "deluge-web" ]]; then 17 | DELUGE_OPTS+=("--loglevel" "warning") 18 | fi 19 | 20 | exec ${DELUGE_BIN} "${DELUGE_OPTS[@]}" "$@" 21 | -------------------------------------------------------------------------------- /apps/deluge/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | deluged: 5 | running: true 6 | port: 7 | tcp:58846: 8 | listening: true 9 | file: 10 | /usr/bin/python3: 11 | exists: true 12 | /usr/bin/unrar: 13 | exists: true 14 | -------------------------------------------------------------------------------- /apps/emby/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/ubuntu:24.04 4 | ARG TARGETARCH 5 | ARG VERSION 6 | 7 | ENV DEBIAN_FRONTEND="noninteractive" \ 8 | NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" 9 | 10 | USER root 11 | 12 | RUN \ 13 | apt-get update \ 14 | && \ 15 | apt-get install -y --no-install-recommends --no-install-suggests \ 16 | bash \ 17 | ca-certificates \ 18 | catatonit \ 19 | curl \ 20 | jq \ 21 | nano \ 22 | tzdata \ 23 | && \ 24 | curl -fsSL -o /tmp/emby.deb \ 25 | "https://github.com/MediaBrowser/Emby.Releases/releases/download/${VERSION}/emby-server-deb_${VERSION}_${TARGETARCH}.deb" \ 26 | && \ 27 | mkdir -p /app/bin /tmp/emby \ 28 | && dpkg-deb -xv /tmp/emby.deb /tmp/emby/ \ 29 | && mv -t /app/bin/ /tmp/emby/opt/emby-server/* \ 30 | && chown -R root:root /app && chmod -R 755 /app \ 31 | && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ 32 | && apt-get autoremove -y \ 33 | && apt-get clean \ 34 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 35 | 36 | COPY . / 37 | 38 | USER nobody:nogroup 39 | WORKDIR /config 40 | VOLUME ["/config"] 41 | 42 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 43 | -------------------------------------------------------------------------------- /apps/emby/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "emby" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=MediaBrowser/Emby.Releases versioning=loose 9 | default = "4.8.11.0" 10 | } 11 | 12 | group "default" { 13 | targets = ["image-local"] 14 | } 15 | 16 | variable "SOURCE" { 17 | default = "https://github.com/MediaBrowser/Emby.Releases" 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/emby/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | APP_DIR="/app/bin" 4 | 5 | export AMDGPU_IDS="${APP_DIR}/extra/share/libdrm/amdgpu.ids" 6 | export FONTCONFIG_PATH="${APP_DIR}/etc/fonts" 7 | export LD_LIBRARY_PATH="${APP_DIR}/lib:${APP_DIR}/extra/lib" 8 | export OCL_ICD_VENDORS="${APP_DIR}/extra/etc/OpenCL/vendors" 9 | export PCI_IDS_PATH="${APP_DIR}/share/hwdata/pci.ids" 10 | export SSL_CERT_FILE="${APP_DIR}/etc/ssl/certs/ca-certificates.crt" 11 | if [ -d "/lib/x86_64-linux-gnu" ]; then 12 | export LIBVA_DRIVERS_PATH="/usr/lib/x86_64-linux-gnu/dri:${APP_DIR}/extra/lib/dri" 13 | fi 14 | 15 | exec \ 16 | /app/bin/system/EmbyServer \ 17 | -programdata /config \ 18 | -ffdetect /app/bin/bin/ffdetect \ 19 | -ffmpeg /app/bin/bin/ffmpeg \ 20 | -ffprobe /app/bin/bin/ffprobe \ 21 | -restartexitcode 3 \ 22 | "$@" 23 | -------------------------------------------------------------------------------- /apps/emby/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | EmbyServer: 5 | running: true 6 | port: 7 | tcp6:8096: 8 | listening: true 9 | http: 10 | http://localhost:8096: 11 | status: 200 12 | -------------------------------------------------------------------------------- /apps/esphome/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/python:3.13-alpine3.22 4 | ARG VERSION 5 | 6 | ENV \ 7 | CRYPTOGRAPHY_DONT_BUILD_RUST=1 \ 8 | PIP_BREAK_SYSTEM_PACKAGES=1 \ 9 | PIP_DISABLE_PIP_VERSION_CHECK=1 \ 10 | PIP_NO_CACHE_DIR=1 \ 11 | PIP_ROOT_USER_ACTION=ignore \ 12 | PYTHONDONTWRITEBYTECODE=1 \ 13 | PYTHONUNBUFFERED=1 \ 14 | UV_NO_CACHE=true \ 15 | UV_SYSTEM_PYTHON=true \ 16 | UV_EXTRA_INDEX_URL="https://wheels.home-assistant.io/musllinux/" 17 | 18 | ENV \ 19 | HOME="/config" \ 20 | PLATFORMIO_CORE_DIR=/cache/pio \ 21 | ESPHOME_BUILD_PATH=/cache/build \ 22 | ESPHOME_DATA_DIR=/cache/data 23 | 24 | USER root 25 | WORKDIR /app 26 | 27 | RUN \ 28 | apk add --no-cache \ 29 | build-base \ 30 | catatonit \ 31 | gcompat \ 32 | git \ 33 | && pip install uv \ 34 | && uv pip install \ 35 | setuptools \ 36 | "esphome[displays]==${VERSION}" \ 37 | && rm -rf /tmp/* 38 | 39 | COPY . / 40 | 41 | USER nobody:nogroup 42 | 43 | WORKDIR /config 44 | 45 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 46 | CMD ["dashboard", "/config"] 47 | -------------------------------------------------------------------------------- /apps/esphome/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "esphome" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=pypi depName=esphome 9 | default = "2025.5.2" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/esphome/esphome" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/esphome/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PLATFORMIO_CORE_DIR=${PLATFORMIO_CORE_DIR:-/cache/pio} 4 | ESPHOME_BUILD_PATH=${ESPHOME_BUILD_PATH:-/cache/build} 5 | ESPHOME_DATA_DIR=${ESPHOME_DATA_DIR:-/cache/data} 6 | 7 | # Make sure cache folders exist 8 | mkdir -p "${PLATFORMIO_CORE_DIR}" 9 | mkdir -p "${ESPHOME_BUILD_PATH}" 10 | mkdir -p "${ESPHOME_DATA_DIR}" 11 | 12 | # Prune PIO files 13 | pio system prune --force 14 | 15 | # Launch ESPHome 16 | exec /usr/local/bin/esphome "$@" 17 | -------------------------------------------------------------------------------- /apps/esphome/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | esphome: 5 | running: true 6 | port: 7 | tcp:6052: 8 | listening: true 9 | http: 10 | http://localhost:6052: 11 | status: 200 12 | -------------------------------------------------------------------------------- /apps/home-assistant/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | ARG TARGETARCH 4 | ARG ARCH=${TARGETARCH/arm64/aarch64} 5 | FROM ghcr.io/home-assistant/${ARCH}-base-python:3.13-alpine3.21 6 | ARG TARGETARCH 7 | ARG ARCH=${TARGETARCH/arm64/aarch64} 8 | ARG VERSION 9 | 10 | ENV \ 11 | CRYPTOGRAPHY_DONT_BUILD_RUST=1 \ 12 | PIP_BREAK_SYSTEM_PACKAGES=1 \ 13 | PIP_DISABLE_PIP_VERSION_CHECK=1 \ 14 | PIP_NO_CACHE_DIR=1 \ 15 | PIP_ROOT_USER_ACTION=ignore \ 16 | PYTHONDONTWRITEBYTECODE=1 \ 17 | PYTHONUNBUFFERED=1 \ 18 | UV_NO_CACHE=true \ 19 | UV_SYSTEM_PYTHON=true \ 20 | UV_EXTRA_INDEX_URL="https://wheels.home-assistant.io/musllinux-index/" 21 | 22 | ENV HOME="/config" \ 23 | PYTHONUSERBASE="/usr/local" \ 24 | VENV_FOLDER="/config/.venv" 25 | 26 | USER root 27 | WORKDIR /app 28 | 29 | RUN \ 30 | apk add --no-cache \ 31 | bash \ 32 | binutils \ 33 | bluez \ 34 | bluez-deprecated \ 35 | bluez-libs \ 36 | ca-certificates \ 37 | catatonit \ 38 | coreutils \ 39 | cups-libs \ 40 | curl \ 41 | eudev-libs \ 42 | ffmpeg \ 43 | git \ 44 | grep \ 45 | hwdata-usb \ 46 | imlib2 \ 47 | iperf3 \ 48 | iputils \ 49 | jq \ 50 | libcap \ 51 | libftdi1 \ 52 | libgpiod \ 53 | libturbojpeg \ 54 | libpulse \ 55 | libstdc++ \ 56 | libxslt \ 57 | libzbar \ 58 | mailcap \ 59 | mariadb-connector-c \ 60 | nano \ 61 | net-tools \ 62 | nmap \ 63 | openssh-client \ 64 | openssl \ 65 | pianobar \ 66 | postgresql-libs \ 67 | pulseaudio-alsa \ 68 | socat \ 69 | tiff \ 70 | tzdata \ 71 | unzip \ 72 | xz \ 73 | && \ 74 | apk add --no-cache --virtual=.build-deps \ 75 | autoconf \ 76 | build-base \ 77 | libffi-dev \ 78 | && \ 79 | pip install uv \ 80 | && \ 81 | curl -fsSL "https://github.com/home-assistant/core/archive/${VERSION}.tar.gz" \ 82 | | tar xzf - -C /tmp --strip-components=1 \ 83 | && \ 84 | HOME_ASSISTANT_BASE=$(curl -fsSL "https://raw.githubusercontent.com/home-assistant/core/${VERSION}/build.yaml" | grep "${ARCH}: " | cut -d ":" -f3) \ 85 | && \ 86 | uv pip install --only-binary=:all: \ 87 | --requirement "https://raw.githubusercontent.com/home-assistant/docker/${HOME_ASSISTANT_BASE}/requirements.txt" \ 88 | && \ 89 | uv pip install --only-binary=:all: --requirement /tmp/requirements_all.txt \ 90 | && \ 91 | uv pip install --only-binary=:all: homeassistant=="${VERSION}" \ 92 | && curl -fsSL -o /bin/go2rtc \ 93 | "https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_linux_${TARGETARCH}" \ 94 | && chmod +x /bin/go2rtc \ 95 | && mkdir -p /config && chown nobody:nogroup -R /config \ 96 | && apk del --purge .build-deps \ 97 | && rm -rf /root/.cache /root/.cargo /tmp/* 98 | 99 | COPY . / 100 | 101 | USER nobody:nogroup 102 | WORKDIR /config 103 | VOLUME ["/config"] 104 | 105 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 106 | -------------------------------------------------------------------------------- /apps/home-assistant/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "home-assistant" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=pypi depName=homeassistant 9 | default = "2025.5.3" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/home-assistant/core" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/home-assistant/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | unset UV_SYSTEM_PYTHON 4 | 5 | mkdir -p "${VENV_FOLDER}" 6 | uv venv --system-site-packages --link-mode=copy --allow-existing "${VENV_FOLDER}" 7 | source "${VENV_FOLDER}/bin/activate" 8 | 9 | site_packages=$(python -c "import sysconfig; print(sysconfig.get_path('purelib'))") 10 | uv pip install --no-index --find-links="${site_packages}" uv 11 | 12 | ln -sf /proc/self/fd/1 /config/home-assistant.log 13 | 14 | exec \ 15 | python3 -m homeassistant \ 16 | --config /config \ 17 | "$@" 18 | -------------------------------------------------------------------------------- /apps/home-assistant/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | python3: 5 | running: true 6 | file: 7 | /usr/local/bin/hass: 8 | exists: true 9 | port: 10 | tcp6:8123: 11 | listening: true 12 | http: 13 | http://localhost:8123: 14 | status: 200 15 | -------------------------------------------------------------------------------- /apps/irqbalance/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG VERSION 5 | USER root 6 | RUN \ 7 | apk add --no-cache \ 8 | bash \ 9 | irqbalance=="${VERSION}" \ 10 | && rm -rf /tmp/* 11 | ENTRYPOINT ["/usr/sbin/irqbalance", "--foreground"] 12 | -------------------------------------------------------------------------------- /apps/irqbalance/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "irqbalance" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=repology depName=alpine_3_22/irqbalance 9 | default = "1.9.4-r1" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/irqbalance/irqbalance" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/irqbalance/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | irqbalance: 5 | running: true 6 | file: 7 | /usr/sbin/irqbalance: 8 | exists: true 9 | -------------------------------------------------------------------------------- /apps/it-tools/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/node:22-alpine AS builder 4 | ARG VERSION 5 | WORKDIR /tmp 6 | ADD https://github.com/CorentinTh/it-tools.git#${VERSION} . 7 | RUN npm install -g corepack@latest \ 8 | && corepack enable \ 9 | && corepack prepare pnpm@latest --activate \ 10 | && pnpm install --prefer-offline \ 11 | && pnpm build 12 | 13 | FROM ghcr.io/nginxinc/nginx-unprivileged:1.27-alpine 14 | ENV NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE=1 15 | COPY --from=builder --chown=nginx:nginx /tmp/dist/ /usr/share/nginx/html 16 | USER nginx 17 | WORKDIR /usr/share/nginx/html 18 | -------------------------------------------------------------------------------- /apps/it-tools/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "it-tools" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=CorentinTh/it-tools 9 | default = "v2024.10.22-7ca5933" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/CorentinTh/it-tools" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/it-tools/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | nginx: 5 | running: true 6 | port: 7 | tcp:8080: 8 | listening: true 9 | http: 10 | http://localhost:8080: 11 | status: 200 12 | -------------------------------------------------------------------------------- /apps/jackett/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG TARGETARCH 5 | ARG TARGETARCH=${TARGETARCH/arm64/ARM64} 6 | ARG TARGETARCH=${TARGETARCH/amd64/AMDx64} 7 | ARG VERSION 8 | 9 | ENV XDG_CONFIG_HOME=/config 10 | 11 | RUN \ 12 | apk add --no-cache \ 13 | bash \ 14 | ca-certificates \ 15 | catatonit \ 16 | coreutils \ 17 | curl \ 18 | icu-data-full \ 19 | icu-libs \ 20 | jq \ 21 | libintl \ 22 | nano \ 23 | tzdata \ 24 | && mkdir -p /app/bin \ 25 | && curl -fsSL "https://github.com/Jackett/Jackett/releases/download/${VERSION}/Jackett.Binaries.LinuxMusl${TARGETARCH}.tar.gz" \ 26 | | tar xzf - -C /app/bin --strip-components 1 \ 27 | && chown -R root:root /app && chmod -R 755 /app \ 28 | && rm -rf /tmp/* /app/bin/JackettUpdater* 29 | 30 | COPY . / 31 | 32 | USER nobody:nogroup 33 | WORKDIR /config 34 | VOLUME ["/config"] 35 | 36 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 37 | -------------------------------------------------------------------------------- /apps/jackett/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "jackett" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=Jackett/Jackett 9 | default = "v0.22.1987" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/Jackett/Jackett" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/jackett/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec \ 4 | /app/bin/jackett \ 5 | --NoUpdates \ 6 | --ListenPublic \ 7 | "$@" 8 | -------------------------------------------------------------------------------- /apps/jackett/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | jackett: 5 | running: true 6 | port: 7 | tcp6:9117: 8 | listening: true 9 | http: 10 | http://localhost:9117: 11 | status: 400 12 | body: 13 | - Cookies required 14 | -------------------------------------------------------------------------------- /apps/jbops/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/python:3.13-alpine3.22 4 | ARG VERSION 5 | 6 | ENV \ 7 | PYTHONDONTWRITEBYTECODE=1 \ 8 | PYTHONUNBUFFERED=1 \ 9 | PIP_ROOT_USER_ACTION=ignore \ 10 | PIP_NO_CACHE_DIR=1 \ 11 | PIP_DISABLE_PIP_VERSION_CHECK=1 \ 12 | PIP_BREAK_SYSTEM_PACKAGES=1 \ 13 | CRYPTOGRAPHY_DONT_BUILD_RUST=1 14 | 15 | ENV PLEXAPI_CONFIG_PATH="/config/config.ini" \ 16 | JBOPS__SCRIPT_PATH="fun/plexapi_haiku.py" 17 | 18 | USER root 19 | WORKDIR /app 20 | 21 | RUN apk add --no-cache \ 22 | bash \ 23 | ca-certificates \ 24 | catatonit \ 25 | coreutils \ 26 | curl \ 27 | jq \ 28 | nano \ 29 | tzdata \ 30 | && \ 31 | apk add --no-cache --virtual=.build-deps \ 32 | build-base \ 33 | libffi-dev \ 34 | openssl-dev \ 35 | musl-dev \ 36 | git \ 37 | && \ 38 | git clone --single-branch --branch "${VERSION}" https://github.com/blacktwin/JBOPS.git . \ 39 | && \ 40 | pip install --upgrade --requirement requirements.txt \ 41 | && chown -R root:root /app && chmod -R 755 /app \ 42 | && apk del --purge .build-deps \ 43 | && rm -rf /root/.cache /root/.cargo /tmp/* /app/maps 44 | 45 | COPY . / 46 | 47 | USER nobody:nogroup 48 | 49 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 50 | -------------------------------------------------------------------------------- /apps/jbops/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "jbops" 5 | } 6 | 7 | variable "VERSION" { 8 | default = "master" 9 | } 10 | 11 | variable "SOURCE" { 12 | default = "https://github.com/blacktwin/JBOPS" 13 | } 14 | 15 | group "default" { 16 | targets = ["image-local"] 17 | } 18 | 19 | target "image" { 20 | inherits = ["docker-metadata-action"] 21 | args = { 22 | VERSION = "${VERSION}" 23 | } 24 | labels = { 25 | "org.opencontainers.image.source" = "${SOURCE}" 26 | } 27 | } 28 | 29 | target "image-local" { 30 | inherits = ["image"] 31 | output = ["type=docker"] 32 | tags = ["${APP}:${VERSION}"] 33 | } 34 | 35 | target "image-all" { 36 | inherits = ["image"] 37 | platforms = [ 38 | "linux/amd64", 39 | "linux/arm64" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /apps/jbops/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec \ 4 | /usr/local/bin/python \ 5 | "/app/${JBOPS__SCRIPT_PATH}" \ 6 | "$@" 7 | -------------------------------------------------------------------------------- /apps/jbops/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | schemaVersion: "2.0.0" 3 | fileExistenceTests: 4 | - name: plexapi_haiku 5 | path: /app/fun/plexapi_haiku.py 6 | shouldExist: true 7 | -------------------------------------------------------------------------------- /apps/k8s-sidecar/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | ARG VERSION 4 | FROM ghcr.io/kiwigrid/k8s-sidecar:${VERSION} 5 | USER root 6 | RUN \ 7 | apk add --no-cache \ 8 | bash \ 9 | ca-certificates \ 10 | curl \ 11 | jq \ 12 | tzdata \ 13 | && rm -rf /tmp/* 14 | USER nobody:nogroup 15 | -------------------------------------------------------------------------------- /apps/k8s-sidecar/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "k8s-sidecar" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=docker depName=ghcr.io/kiwigrid/k8s-sidecar 9 | default = "1.30.3" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/kiwigrid/k8s-sidecar" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/k8s-sidecar/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | python: 5 | running: true 6 | -------------------------------------------------------------------------------- /apps/lidarr/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG TARGETARCH 5 | ARG VENDOR 6 | ARG VERSION 7 | 8 | ENV DOTNET_EnableDiagnostics=0 \ 9 | LIDARR__UPDATE__BRANCH=develop 10 | 11 | USER root 12 | WORKDIR /app 13 | 14 | RUN \ 15 | apk add --no-cache \ 16 | bash \ 17 | ca-certificates \ 18 | catatonit \ 19 | chromaprint \ 20 | coreutils \ 21 | curl \ 22 | ffmpeg \ 23 | icu-libs \ 24 | jq \ 25 | libintl \ 26 | nano \ 27 | sqlite-libs \ 28 | tzdata \ 29 | && mkdir -p /app/bin \ 30 | && curl -fsSL "https://lidarr.servarr.com/v1/update/${LIDARR__UPDATE__BRANCH}/updatefile?version=${VERSION}&os=linuxmusl&runtime=netcore&arch=${TARGETARCH/amd64/x64}" \ 31 | | tar xzf - -C /app/bin --strip-components=1 \ 32 | && printf "UpdateMethod=docker\nBranch=%s\nPackageVersion=%s\nPackageAuthor=[%s](https://github.com/%s)\n" "${LIDARR__UPDATE__BRANCH}" "${VERSION}" "${VENDOR}" "${VENDOR}" > /app/package_info \ 33 | && chown -R root:root /app && chmod -R 755 /app \ 34 | && rm -rf /tmp/* /app/bin/Lidarr.Update /app/bin/fpcalc 35 | 36 | COPY . / 37 | 38 | USER nobody:nogroup 39 | WORKDIR /config 40 | VOLUME ["/config"] 41 | 42 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 43 | -------------------------------------------------------------------------------- /apps/lidarr/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "lidarr" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=custom.servarr-develop depName=lidarr versioning=loose 9 | default = "2.12.1.4636" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/Lidarr/Lidarr" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/lidarr/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec \ 4 | /app/bin/Lidarr \ 5 | --nobrowser \ 6 | --data=/config \ 7 | "$@" 8 | -------------------------------------------------------------------------------- /apps/lidarr/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | Lidarr: 5 | running: true 6 | port: 7 | tcp6:8686: 8 | listening: true 9 | http: 10 | http://localhost:8686: 11 | status: 200 12 | timeout: 5000 13 | -------------------------------------------------------------------------------- /apps/nzbget/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/python:3.13-alpine3.22 4 | ARG VERSION 5 | 6 | ENV \ 7 | CRYPTOGRAPHY_DONT_BUILD_RUST=1 \ 8 | PIP_BREAK_SYSTEM_PACKAGES=1 \ 9 | PIP_DISABLE_PIP_VERSION_CHECK=1 \ 10 | PIP_NO_CACHE_DIR=1 \ 11 | PIP_ROOT_USER_ACTION=ignore \ 12 | PYTHONDONTWRITEBYTECODE=1 \ 13 | PYTHONUNBUFFERED=1 \ 14 | UV_NO_CACHE=true \ 15 | UV_SYSTEM_PYTHON=true \ 16 | UV_EXTRA_INDEX_URL="https://wheel-index.linuxserver.io/alpine-3.22/" 17 | 18 | USER root 19 | WORKDIR /app 20 | 21 | RUN \ 22 | apk add --no-cache \ 23 | 7zip \ 24 | bash \ 25 | ca-certificates \ 26 | catatonit \ 27 | coreutils \ 28 | curl \ 29 | jq \ 30 | nano \ 31 | tzdata \ 32 | && \ 33 | curl -fsSL -o /tmp/nzbget.run \ 34 | "https://github.com/nzbgetcom/nzbget/releases/download/v${VERSION}/nzbget-${VERSION}-bin-linux.run" \ 35 | && \ 36 | sh /tmp/nzbget.run --destdir /app \ 37 | && pip install uv \ 38 | && uv pip install apprise pynzb requests \ 39 | && chown -R root:root /app && chmod -R 755 /app \ 40 | && pip uninstall --yes uv \ 41 | && rm -rf /root/.cache /root/.cargo /tmp/* 42 | 43 | COPY . / 44 | 45 | COPY --from=ghcr.io/linuxserver/unrar:latest /usr/bin/unrar-alpine /usr/bin/unrar 46 | 47 | USER nobody:nogroup 48 | WORKDIR /config 49 | VOLUME ["/config"] 50 | 51 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 52 | -------------------------------------------------------------------------------- /apps/nzbget/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "nzbget" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=nzbgetcom/nzbget versioning=loose 9 | default = "25.0" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/nzbgetcom/nzbget" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/nzbget/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONFIG_FILE="/config/nzbget.conf" 4 | 5 | if [[ ! -f "${CONFIG_FILE}" ]]; then 6 | cp /app/nzbget.conf "${CONFIG_FILE}" 7 | sed -i \ 8 | -e "s|^MainDir=.*|MainDir=/config/downloads|g" \ 9 | -e "s|^QueueDir=.*|QueueDir=/config/queue|g" \ 10 | -e "s|^LockFile=.*|LockFile=/config/nzbget.lock|g" \ 11 | -e "s|^LogFile=.*|LogFile=/config/nzbget.log|g" \ 12 | -e "s|^ShellOverride=.*|ShellOverride=.py=/usr/bin/python3;.sh=/bin/bash|g" \ 13 | "${CONFIG_FILE}" 14 | fi 15 | 16 | exec \ 17 | /app/nzbget \ 18 | --server \ 19 | --option "OutputMode=log" \ 20 | --configfile "${CONFIG_FILE}" \ 21 | "$@" 22 | -------------------------------------------------------------------------------- /apps/nzbget/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | nzbget: 5 | running: true 6 | port: 7 | tcp:6789: 8 | listening: true 9 | http: 10 | http://localhost:6789: 11 | status: 200 12 | -------------------------------------------------------------------------------- /apps/nzbhydra2/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/amazoncorretto:17-alpine3.21 4 | 5 | ARG TARGETARCH 6 | ARG VENDOR 7 | ARG VERSION 8 | 9 | ENV JAVA_TOOL_OPTIONS="-Xmx256M" 10 | 11 | RUN \ 12 | apk add --no-cache \ 13 | bash \ 14 | ca-certificates \ 15 | catatonit \ 16 | coreutils \ 17 | curl \ 18 | freetype \ 19 | jq \ 20 | nano \ 21 | tzdata \ 22 | unzip \ 23 | && mkdir -p /app/bin \ 24 | && curl -fsSL -o /tmp/nzbhydra2.zip \ 25 | "https://github.com/theotherp/nzbhydra2/releases/download/${VERSION}/nzbhydra2-${VERSION#*v}-generic.zip" \ 26 | && unzip -q /tmp/nzbhydra2.zip -d /tmp \ 27 | && cp /tmp/lib/core-${VERSION#*v}-exec.jar /app/bin/nzbhydra2.jar \ 28 | && mkdir -p /defaults \ 29 | && curl -fsSL -o /defaults/nzbhydra.yml \ 30 | "https://raw.githubusercontent.com/theotherp/nzbhydra2/${VERSION}/core/src/main/resources/config/baseConfig.yml" \ 31 | && sed -i 's/mapIpToHost: true/mapIpToHost: false/' /defaults/nzbhydra.yml \ 32 | && sed -i 's/checkOpenPort: true/checkOpenPort: false/' /defaults/nzbhydra.yml \ 33 | && chown -R root:root /app && chmod -R 755 /app \ 34 | && rm -rf /tmp/* 35 | 36 | COPY . / 37 | 38 | USER nobody:nogroup 39 | WORKDIR /config 40 | VOLUME ["/config"] 41 | 42 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 43 | -------------------------------------------------------------------------------- /apps/nzbhydra2/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "nzbhydra2" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=theotherp/nzbhydra2 9 | default = "v7.14.1" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/theotherp/nzbhydra2" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/nzbhydra2/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir -p /config/logs 4 | 5 | if [[ ! -f /config/nzbhydra.yml ]]; then 6 | cp /defaults/nzbhydra.yml /config/nzbhydra.yml 7 | fi 8 | 9 | exec \ 10 | java \ 11 | -DfromWrapper \ 12 | -noverify \ 13 | -XX:TieredStopAtLevel=1 \ 14 | -XX:+HeapDumpOnOutOfMemoryError \ 15 | -XX:HeapDumpPath=/config/logs \ 16 | -Xlog:gc:/config/logs/gclog-"$(date +"%F_%H-%M-%S")".log::filecount=5,filesize=5000 \ 17 | -Dspring.output.ansi.enabled=ALWAYS \ 18 | -Dsun.security.pkcs11.enable-solaris=false \ 19 | -Dfile.encoding=UTF8 \ 20 | -DinternalApiKey="${NZBHYDRA2__API_KEY:-}" \ 21 | -jar /app/bin/nzbhydra2.jar \ 22 | --datafolder /config \ 23 | "$@" 24 | -------------------------------------------------------------------------------- /apps/nzbhydra2/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | java: 5 | running: true 6 | port: 7 | tcp6:5076: 8 | listening: true 9 | http: 10 | http://localhost:5076: 11 | status: 200 12 | -------------------------------------------------------------------------------- /apps/opentofu-runner/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 AS builder 4 | ARG TARGETARCH 5 | ARG VERSION 6 | RUN apk add --no-cache curl \ 7 | && curl -fsSL "https://github.com/opentofu/opentofu/releases/download/v${VERSION}/tofu_${VERSION}_linux_${TARGETARCH}.tar.gz" \ 8 | | tar xzf - -C /tmp 9 | 10 | FROM ghcr.io/flux-iac/tf-runner:v0.16.0-rc.5 11 | USER root 12 | COPY --from=builder --chown=65532:65532 --chmod=755 /tmp/tofu /usr/local/bin/terraform 13 | USER 65532:65532 14 | -------------------------------------------------------------------------------- /apps/opentofu-runner/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "opentofu-runner" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=opentofu/opentofu 9 | default = "1.9.1" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/opentofu/opentofu" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/opentofu-runner/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | schemaVersion: "2.0.0" 3 | fileExistenceTests: 4 | - name: terraform 5 | path: /usr/local/bin/terraform 6 | shouldExist: true 7 | -------------------------------------------------------------------------------- /apps/plex/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/ubuntu:24.04 4 | ARG TARGETARCH 5 | ARG VENDOR 6 | ARG VERSION 7 | 8 | ENV DEBIAN_FRONTEND="noninteractive" \ 9 | NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" \ 10 | PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR="/config/Library/Application Support" \ 11 | PLEX_MEDIA_SERVER_HOME="/usr/lib/plexmediaserver" \ 12 | PLEX_MEDIA_SERVER_MAX_PLUGIN_PROCS="6" \ 13 | PLEX_MEDIA_SERVER_INFO_VENDOR="Docker" \ 14 | PLEX_MEDIA_SERVER_INFO_DEVICE="Docker Container (${VENDOR})" 15 | 16 | USER root 17 | WORKDIR /app 18 | 19 | RUN \ 20 | apt-get update \ 21 | && \ 22 | apt-get install -y --no-install-recommends --no-install-suggests \ 23 | bash \ 24 | ca-certificates \ 25 | catatonit \ 26 | coreutils \ 27 | curl \ 28 | jq \ 29 | nano \ 30 | tzdata \ 31 | uuid-runtime \ 32 | xmlstarlet \ 33 | && \ 34 | curl -fsSL -o /tmp/plex.deb \ 35 | "https://downloads.plex.tv/plex-media-server-new/${VERSION}/debian/plexmediaserver_${VERSION}_${TARGETARCH}.deb" \ 36 | && \ 37 | dpkg -i /tmp/plex.deb \ 38 | && chmod -R 755 "${PLEX_MEDIA_SERVER_HOME}" \ 39 | && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ 40 | && apt-get autoremove -y \ 41 | && apt-get clean \ 42 | && rm -rf /etc/default/plexmediaserver /tmp/* /var/lib/apt/lists/* /var/tmp/ 43 | 44 | COPY . / 45 | 46 | USER nobody:nogroup 47 | WORKDIR /config 48 | VOLUME ["/config"] 49 | 50 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 51 | -------------------------------------------------------------------------------- /apps/plex/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "plex" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=custom.plex depName=plex versioning=loose 9 | default = "1.41.7.9823-59f304c16" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/plexinc/pms-docker" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/plex/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export PLEX_MEDIA_SERVER_INFO_MODEL=$(uname -m) 4 | export PLEX_MEDIA_SERVER_INFO_PLATFORM_VERSION=$(uname -r) 5 | 6 | function getPref { 7 | local key="$1" 8 | xmlstarlet sel -T -t -m "/Preferences" -v "@${key}" -n "${prefFile}" 9 | } 10 | 11 | function setPref { 12 | local key="$1" 13 | local value="$2" 14 | count="$(xmlstarlet sel -t -v "count(/Preferences/@${key})" "${prefFile}")" 15 | count=$((count + 0)) 16 | if [[ $count -gt 0 ]]; then 17 | xmlstarlet ed --inplace --update "/Preferences/@${key}" -v "${value}" "${prefFile}" 18 | else 19 | xmlstarlet ed --inplace --insert "/Preferences" --type attr -n "${key}" -v "${value}" "${prefFile}" 20 | fi 21 | } 22 | 23 | prefFile="${PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR}/Plex Media Server/Preferences.xml" 24 | 25 | # Create empty Preferences.xml file if it doesn't exist already 26 | if [ ! -e "${prefFile}" ]; then 27 | echo "Creating pref shell" 28 | mkdir -p "$(dirname "${prefFile}")" 29 | cat > "${prefFile}" <<-EOF 30 | 31 | 32 | EOF 33 | fi 34 | 35 | # Setup Server's client identifier 36 | serial="$(getPref "MachineIdentifier")" 37 | if [[ -z "${serial}" ]]; then 38 | serial="$(cat /proc/sys/kernel/random/uuid)" 39 | setPref "MachineIdentifier" "${serial}" 40 | fi 41 | clientId="$(getPref "ProcessedMachineIdentifier")" 42 | if [[ -z "${clientId}" ]]; then 43 | clientId="$(echo -n "${serial}- Plex Media Server" | sha1sum | cut -b 1-40)" 44 | setPref "ProcessedMachineIdentifier" "${clientId}" 45 | fi 46 | 47 | # Get server token and only turn claim token into server token if we have former but not latter. 48 | token="$(getPref "PlexOnlineToken")" 49 | if [[ -n "${PLEX_CLAIM_TOKEN}" ]] && [[ -z "${token}" ]]; then 50 | echo "Attempting to obtain server token from claim token..." 51 | loginInfo="$(curl -fsSL -X POST \ 52 | -H 'X-Plex-Client-Identifier: '"${clientId}" \ 53 | -H 'X-Plex-Product: Plex Media Server'\ 54 | -H 'X-Plex-Version: 1.1' \ 55 | -H 'X-Plex-Provides: server' \ 56 | -H 'X-Plex-Platform: Linux' \ 57 | -H 'X-Plex-Platform-Version: 1.0' \ 58 | -H 'X-Plex-Device-Name: PlexMediaServer' \ 59 | -H 'X-Plex-Device: Linux' \ 60 | "https://plex.tv/api/claim/exchange?token=${PLEX_CLAIM_TOKEN}")" 61 | token="$(echo "$loginInfo" | sed -n 's/.*\(.*\)<\/authentication-token>.*/\1/p')" 62 | 63 | if [[ "$token" ]]; then 64 | echo "Token obtained successfully!" 65 | setPref "PlexOnlineToken" "${token}" 66 | fi 67 | fi 68 | 69 | # Set other preferences 70 | [[ -n "${ADVERTISE_IP}" ]] && PLEX_ADVERTISE_URL=${ADVERTISE_IP} 71 | if [[ -n "${PLEX_ADVERTISE_URL}" ]]; then 72 | echo "Setting customConnections to: ${PLEX_ADVERTISE_URL}" 73 | setPref "customConnections" "${PLEX_ADVERTISE_URL}" 74 | fi 75 | 76 | [[ -n "${ALLOWED_NETWORKS}" ]] && PLEX_NO_AUTH_NETWORKS=${ALLOWED_NETWORKS} 77 | if [[ -n "${PLEX_NO_AUTH_NETWORKS}" ]]; then 78 | echo "Setting allowedNetworks to: ${PLEX_NO_AUTH_NETWORKS}" 79 | setPref "allowedNetworks" "${PLEX_NO_AUTH_NETWORKS}" 80 | fi 81 | 82 | # Set transcoder directory if not yet set 83 | if [[ -z "$(getPref "TranscoderTempDirectory")" ]]; then 84 | echo "Setting TranscoderTempDirectory to: /transcode" 85 | setPref "TranscoderTempDirectory" "/transcode" 86 | fi 87 | 88 | # Parse list of all exported variables that start with PLEX_PREFERENCE_ 89 | # The format of which is PLEX_PREFERENCE_="Key=Value" 90 | # Where Key is the EXACT key to use in the Plex Preference file 91 | # And Value is the EXACT value to use in the Plex Preference file for that key. 92 | # Please note it looks like many of the key's are camelCase in some fashion. 93 | # Additionally there are likely some preferences where environment variable injection 94 | # doesn't really work for. 95 | for var in "${!PLEX_PREFERENCE_@}"; do 96 | value="${!var}" 97 | PreferenceValue="${value#*=}" 98 | PreferenceKey="${value%=*}" 99 | setPref "${PreferenceKey}" "${PreferenceValue}" 100 | done 101 | 102 | # Remove pid file 103 | rm -f "${PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR}/Plex Media Server/plexmediaserver.pid" 104 | 105 | # Purge Codecs folder 106 | if [[ "${PLEX_PURGE_CODECS}" == "true" ]]; then 107 | echo "Purging Codecs folder..." 108 | find "${PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR}/Plex Media Server/Codecs" -mindepth 1 -not -name '.device-id' -print -delete 109 | fi 110 | 111 | exec \ 112 | /usr/lib/plexmediaserver/Plex\ Media\ Server \ 113 | "$@" 114 | -------------------------------------------------------------------------------- /apps/plex/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | port: 4 | tcp6:32400: 5 | listening: true 6 | -------------------------------------------------------------------------------- /apps/postgres-init/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG VERSION 5 | USER root 6 | RUN \ 7 | apk add --no-cache \ 8 | bash \ 9 | ca-certificates \ 10 | catatonit \ 11 | postgresql17-client=="${VERSION}" \ 12 | && rm -rf /tmp/* 13 | COPY . / 14 | USER nobody:nogroup 15 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 16 | -------------------------------------------------------------------------------- /apps/postgres-init/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "postgres-init" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=repology depName=alpine_3_22/postgresql17-client versioning=loose 9 | default = "17.5-r0" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/postgres/postgres" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/postgres-init/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This is most commonly set to the user 'postgres' 4 | export INIT_POSTGRES_SUPER_USER=${INIT_POSTGRES_SUPER_USER:-postgres} 5 | export INIT_POSTGRES_PORT=${INIT_POSTGRES_PORT:-5432} 6 | export INIT_POSTGRES_UTF8=${INIT_POSTGRES_UTF8:-"false"} 7 | 8 | if [[ -z "${INIT_POSTGRES_HOST}" || 9 | -z "${INIT_POSTGRES_SUPER_PASS}" || 10 | -z "${INIT_POSTGRES_USER}" || 11 | -z "${INIT_POSTGRES_PASS}" || 12 | -z "${INIT_POSTGRES_DBNAME}" 13 | ]]; then 14 | printf "\e[1;32m%-6s\e[m\n" "Invalid configuration - missing a required environment variable" 15 | [[ -z "${INIT_POSTGRES_HOST}" ]] && printf "\e[1;32m%-6s\e[m\n" "INIT_POSTGRES_HOST: unset" 16 | [[ -z "${INIT_POSTGRES_SUPER_PASS}" ]] && printf "\e[1;32m%-6s\e[m\n" "INIT_POSTGRES_SUPER_PASS: unset" 17 | [[ -z "${INIT_POSTGRES_USER}" ]] && printf "\e[1;32m%-6s\e[m\n" "INIT_POSTGRES_USER: unset" 18 | [[ -z "${INIT_POSTGRES_PASS}" ]] && printf "\e[1;32m%-6s\e[m\n" "INIT_POSTGRES_PASS: unset" 19 | [[ -z "${INIT_POSTGRES_DBNAME}" ]] && printf "\e[1;32m%-6s\e[m\n" "INIT_POSTGRES_DBNAME: unset" 20 | exit 1 21 | fi 22 | 23 | # These env are for the psql CLI 24 | export PGHOST="${INIT_POSTGRES_HOST}" 25 | export PGUSER="${INIT_POSTGRES_SUPER_USER}" 26 | export PGPASSWORD="${INIT_POSTGRES_SUPER_PASS}" 27 | export PGPORT="${INIT_POSTGRES_PORT}" 28 | 29 | until pg_isready; do 30 | printf "\e[1;32m%-6s\e[m\n" "Waiting for Host '${PGHOST}' on port '${PGPORT}' ..." 31 | sleep 1 32 | done 33 | 34 | user_exists=$(\ 35 | psql \ 36 | --tuples-only \ 37 | --csv \ 38 | --command "SELECT 1 FROM pg_roles WHERE rolname = '${INIT_POSTGRES_USER}'" 39 | ) 40 | 41 | if [[ -z "${user_exists}" ]]; then 42 | printf "\e[1;32m%-6s\e[m\n" "Create User ${INIT_POSTGRES_USER} ..." 43 | createuser ${INIT_POSTGRES_USER_FLAGS} "${INIT_POSTGRES_USER}" 44 | fi 45 | 46 | printf "\e[1;32m%-6s\e[m\n" "Update password for user ${INIT_POSTGRES_USER} ..." 47 | psql --command "alter user \"${INIT_POSTGRES_USER}\" with encrypted password '${INIT_POSTGRES_PASS}';" 48 | 49 | for dbname in ${INIT_POSTGRES_DBNAME}; do 50 | database_exists=$(\ 51 | psql \ 52 | --tuples-only \ 53 | --csv \ 54 | --command "SELECT 1 FROM pg_database WHERE datname = '${dbname}'" 55 | ) 56 | if [[ -z "${database_exists}" ]]; then 57 | if [[ "${INIT_POSTGRES_UTF8}" == "true" ]]; then 58 | printf "\e[1;32m%-6s\e[m\n" "Create Database ${dbname} with UTF8 encoding ..." 59 | createdb --template template0 --encoding UTF8 --owner "${INIT_POSTGRES_USER}" "${dbname}" 60 | else 61 | printf "\e[1;32m%-6s\e[m\n" "Create Database ${dbname} ..." 62 | createdb --owner "${INIT_POSTGRES_USER}" "${dbname}" 63 | fi 64 | database_init_file="/initdb/${dbname}.sql" 65 | if [[ -f "${database_init_file}" ]]; then 66 | printf "\e[1;32m%-6s\e[m\n" "Initialize Database ..." 67 | psql \ 68 | --dbname "${dbname}" \ 69 | --echo-all \ 70 | --file "${database_init_file}" 71 | fi 72 | fi 73 | printf "\e[1;32m%-6s\e[m\n" "Update User Privileges on Database ..." 74 | psql --command "grant all privileges on database \"${dbname}\" to \"${INIT_POSTGRES_USER}\";" 75 | done 76 | -------------------------------------------------------------------------------- /apps/postgres-init/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | schemaVersion: "2.0.0" 3 | fileExistenceTests: 4 | - name: psql 5 | path: /usr/libexec/postgresql17/psql 6 | shouldExist: true 7 | -------------------------------------------------------------------------------- /apps/prowlarr/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG TARGETARCH 5 | ARG VENDOR 6 | ARG VERSION 7 | 8 | ENV DOTNET_EnableDiagnostics=0 \ 9 | PROWLARR__UPDATE__BRANCH=develop 10 | 11 | USER root 12 | WORKDIR /app 13 | 14 | RUN \ 15 | apk add --no-cache \ 16 | bash \ 17 | ca-certificates \ 18 | catatonit \ 19 | coreutils \ 20 | curl \ 21 | icu-libs \ 22 | jq \ 23 | libintl \ 24 | nano \ 25 | sqlite-libs \ 26 | tzdata \ 27 | && mkdir -p /app/bin \ 28 | && curl -fsSL "https://prowlarr.servarr.com/v1/update/${PROWLARR__UPDATE__BRANCH}/updatefile?version=${VERSION}&os=linuxmusl&runtime=netcore&arch=${TARGETARCH/amd64/x64}" \ 29 | | tar xzf - -C /app/bin --strip-components=1 \ 30 | && printf "UpdateMethod=docker\nBranch=%s\nPackageVersion=%s\nPackageAuthor=[%s](https://github.com/%s)\n" "${PROWLARR__UPDATE__BRANCH}" "${VERSION}" "${VENDOR}" "${VENDOR}" > /app/package_info \ 31 | && chown -R root:root /app && chmod -R 755 /app \ 32 | && rm -rf /tmp/* /app/bin/Prowlarr.Update 33 | 34 | COPY . / 35 | 36 | USER nobody:nogroup 37 | WORKDIR /config 38 | VOLUME ["/config"] 39 | 40 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 41 | -------------------------------------------------------------------------------- /apps/prowlarr/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "prowlarr" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=custom.servarr-develop depName=prowlarr versioning=loose 9 | default = "1.37.0.5076" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/Prowlarr/Prowlarr" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/prowlarr/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec \ 4 | /app/bin/Prowlarr \ 5 | --nobrowser \ 6 | --data=/config \ 7 | "$@" 8 | -------------------------------------------------------------------------------- /apps/prowlarr/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | Prowlarr: 5 | running: true 6 | port: 7 | tcp6:9696: 8 | listening: true 9 | http: 10 | http://localhost:9696: 11 | status: 200 12 | timeout: 5000 13 | -------------------------------------------------------------------------------- /apps/qbittorrent/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/python:3.13-alpine3.22 4 | ARG TARGETARCH 5 | ARG TARGETARCH=${TARGETARCH/arm64/aarch64} 6 | ARG TARGETARCH=${TARGETARCH/amd64/x86_64} 7 | ARG VERSION 8 | 9 | ENV QBT_CONFIRM_LEGAL_NOTICE=1 \ 10 | HOME="/config" \ 11 | XDG_CONFIG_HOME="/config" \ 12 | XDG_DATA_HOME="/config" 13 | 14 | USER root 15 | WORKDIR /app 16 | 17 | RUN \ 18 | apk add --no-cache \ 19 | 7zip \ 20 | bash \ 21 | ca-certificates \ 22 | catatonit \ 23 | coreutils \ 24 | curl \ 25 | jq \ 26 | nano \ 27 | tzdata \ 28 | && \ 29 | mkdir -p /app \ 30 | && LIBTORRENT=$(curl -sL "https://github.com/userdocs/qbittorrent-nox-static/releases/latest/download/dependency-version.json" | jq -r '.libtorrent_2_0') \ 31 | && curl -fsSL -o /app/qbittorrent-nox "https://github.com/userdocs/qbittorrent-nox-static/releases/download/release-${VERSION}_v${LIBTORRENT}/${TARGETARCH}-qbittorrent-nox" \ 32 | && chown -R root:root /app/qbittorrent-nox \ 33 | && chmod -R 755 /app/qbittorrent-nox \ 34 | && rm -rf /tmp/* 35 | 36 | COPY . / 37 | 38 | COPY --from=ghcr.io/linuxserver/unrar:latest /usr/bin/unrar-alpine /usr/bin/unrar 39 | 40 | USER nobody:nogroup 41 | WORKDIR /config 42 | VOLUME ["/config"] 43 | 44 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 45 | -------------------------------------------------------------------------------- /apps/qbittorrent/defaults/qBittorrent.conf: -------------------------------------------------------------------------------- 1 | [AutoRun] 2 | enabled=false 3 | program= 4 | 5 | [LegalNotice] 6 | Accepted=true 7 | 8 | [BitTorrent] 9 | Session\AsyncIOThreadsCount=10 10 | Session\DiskCacheSize=-1 11 | Session\DiskIOReadMode=DisableOSCache 12 | Session\DiskIOType=SimplePreadPwrite 13 | Session\DiskIOWriteMode=EnableOSCache 14 | Session\DiskQueueSize=4194304 15 | Session\FilePoolSize=40 16 | Session\HashingThreadsCount=2 17 | Session\Port=50413 18 | Session\ResumeDataStorageType=SQLite 19 | Session\UseOSCache=true 20 | 21 | [Preferences] 22 | Connection\PortRangeMin=6881 23 | Connection\UPnP=false 24 | General\Locale=en 25 | General\UseRandomPort=false 26 | WebUI\Address=* 27 | WebUI\CSRFProtection=false 28 | WebUI\HostHeaderValidation=false 29 | WebUI\LocalHostAuth=false 30 | WebUI\Port=8080 31 | WebUI\ServerDomains=* 32 | WebUI\UseUPnP=false 33 | -------------------------------------------------------------------------------- /apps/qbittorrent/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "qbittorrent" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=custom.qbittorrent depName=qbittorrent 9 | default = "5.1.0" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/qbittorrent/qBittorrent" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/qbittorrent/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONFIG_FILE="/config/qBittorrent/qBittorrent.conf" 4 | LOG_FILE="/config/qBittorrent/logs/qbittorrent.log" 5 | 6 | # Ensure the config file exists, copy default if missing 7 | if [[ ! -f "${CONFIG_FILE}" ]]; then 8 | mkdir -p "${CONFIG_FILE%/*}" 9 | cp /defaults/qBittorrent.conf "${CONFIG_FILE}" 10 | fi 11 | 12 | # Set up log file to redirect to stdout 13 | if [[ ! -f "${LOG_FILE}" ]]; then 14 | mkdir -p "${LOG_FILE%/*}" 15 | ln -sf /proc/self/fd/1 "${LOG_FILE}" 16 | fi 17 | 18 | # Execute qBittorrent 19 | exec /app/qbittorrent-nox "$@" 20 | -------------------------------------------------------------------------------- /apps/qbittorrent/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | qbittorrent-nox: 5 | running: true 6 | port: 7 | tcp6:8080: 8 | listening: true 9 | http: 10 | http://localhost:8080: 11 | status: 200 12 | file: 13 | /usr/local/bin/python: 14 | exists: true 15 | /usr/bin/unrar: 16 | exists: true 17 | -------------------------------------------------------------------------------- /apps/radarr/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG TARGETARCH 5 | ARG VENDOR 6 | ARG VERSION 7 | 8 | ENV DOTNET_EnableDiagnostics=0 \ 9 | RADARR__UPDATE__BRANCH=develop 10 | 11 | USER root 12 | WORKDIR /app 13 | 14 | RUN \ 15 | apk add --no-cache \ 16 | bash \ 17 | ca-certificates \ 18 | catatonit \ 19 | coreutils \ 20 | curl \ 21 | icu-libs \ 22 | jq \ 23 | libintl \ 24 | nano \ 25 | sqlite-libs \ 26 | tzdata \ 27 | && mkdir -p /app/bin \ 28 | && curl -fsSL "https://radarr.servarr.com/v1/update/${RADARR__UPDATE__BRANCH}/updatefile?version=${VERSION}&os=linuxmusl&runtime=netcore&arch=${TARGETARCH/amd64/x64}" \ 29 | | tar xzf - -C /app/bin --strip-components=1 \ 30 | && printf "UpdateMethod=docker\nBranch=%s\nPackageVersion=%s\nPackageAuthor=[%s](https://github.com/%s)\n" "${RADARR__UPDATE__BRANCH}" "${VERSION}" "${VENDOR}" "${VENDOR}" > /app/package_info \ 31 | && chown -R root:root /app && chmod -R 755 /app \ 32 | && rm -rf /tmp/* /app/bin/Radarr.Update 33 | 34 | COPY . / 35 | 36 | USER nobody:nogroup 37 | WORKDIR /config 38 | VOLUME ["/config"] 39 | 40 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 41 | -------------------------------------------------------------------------------- /apps/radarr/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "radarr" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=custom.servarr-develop depName=radarr versioning=loose 9 | default = "5.26.0.10051" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/Radarr/Radarr" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/radarr/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec \ 4 | /app/bin/Radarr \ 5 | --nobrowser \ 6 | --data=/config \ 7 | "$@" 8 | -------------------------------------------------------------------------------- /apps/radarr/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | Radarr: 5 | running: true 6 | port: 7 | tcp6:7878: 8 | listening: true 9 | http: 10 | http://localhost:7878: 11 | status: 200 12 | timeout: 5000 13 | -------------------------------------------------------------------------------- /apps/readarr/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG TARGETARCH 5 | ARG VENDOR 6 | ARG VERSION 7 | 8 | ENV COMPlus_EnableDiagnostics=0 \ 9 | READARR__UPDATE__BRANCH=develop 10 | 11 | USER root 12 | WORKDIR /app 13 | 14 | RUN \ 15 | apk add --no-cache \ 16 | bash \ 17 | ca-certificates \ 18 | catatonit \ 19 | coreutils \ 20 | curl \ 21 | icu-libs \ 22 | jq \ 23 | libintl \ 24 | nano \ 25 | sqlite-libs \ 26 | tzdata \ 27 | && mkdir -p /app/bin \ 28 | && curl -fsSL "https://readarr.servarr.com/v1/update/${READARR__UPDATE__BRANCH}/updatefile?version=${VERSION}&os=linuxmusl&runtime=netcore&arch=${TARGETARCH/amd64/x64}" \ 29 | | tar xzf - -C /app/bin --strip-components=1 \ 30 | && printf "UpdateMethod=docker\nBranch=%s\nPackageVersion=%s\nPackageAuthor=[%s](https://github.com/%s)\n" "${READARR__UPDATE__BRANCH}" "${VERSION}" "${VENDOR}" "${VENDOR}" > /app/package_info \ 31 | && chown -R root:root /app && chmod -R 755 /app \ 32 | && rm -rf /tmp/* /app/bin/Readarr.Update 33 | 34 | COPY . / 35 | 36 | USER nobody:nogroup 37 | WORKDIR /config 38 | VOLUME ["/config"] 39 | 40 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 41 | 42 | LABEL org.opencontainers.image.source="https://github.com/Readarr/Readarr" 43 | -------------------------------------------------------------------------------- /apps/readarr/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "readarr" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=custom.servarr-develop depName=readarr versioning=loose 9 | default = "0.4.16.2793" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/Readarr/Readarr" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/readarr/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec \ 4 | /app/bin/Readarr \ 5 | --nobrowser \ 6 | --data=/config \ 7 | "$@" 8 | -------------------------------------------------------------------------------- /apps/readarr/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | Readarr: 5 | running: true 6 | port: 7 | tcp6:8787: 8 | listening: true 9 | http: 10 | http://localhost:8787: 11 | status: 200 12 | timeout: 5000 13 | -------------------------------------------------------------------------------- /apps/sabnzbd/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/python:3.13-alpine3.22 4 | ARG VERSION 5 | 6 | ENV \ 7 | CRYPTOGRAPHY_DONT_BUILD_RUST=1 \ 8 | PIP_BREAK_SYSTEM_PACKAGES=1 \ 9 | PIP_DISABLE_PIP_VERSION_CHECK=1 \ 10 | PIP_NO_CACHE_DIR=1 \ 11 | PIP_ROOT_USER_ACTION=ignore \ 12 | PYTHONDONTWRITEBYTECODE=1 \ 13 | PYTHONUNBUFFERED=1 \ 14 | UV_NO_CACHE=true \ 15 | UV_SYSTEM_PYTHON=true \ 16 | UV_EXTRA_INDEX_URL="https://wheel-index.linuxserver.io/alpine-3.22/" 17 | 18 | ENV SABNZBD__ADDRESS="[::]" \ 19 | SABNZBD__PORT="8080" 20 | 21 | USER root 22 | WORKDIR /app 23 | 24 | RUN \ 25 | apk add --no-cache \ 26 | 7zip \ 27 | bash \ 28 | ca-certificates \ 29 | catatonit \ 30 | coreutils \ 31 | curl \ 32 | jq \ 33 | nano \ 34 | tzdata \ 35 | && \ 36 | apk add --no-cache --repository="https://dl-cdn.alpinelinux.org/alpine/edge/testing/" \ 37 | par2cmdline-turbo \ 38 | && \ 39 | apk add --no-cache --virtual=.build-deps \ 40 | build-base \ 41 | cargo \ 42 | libffi-dev \ 43 | musl-dev \ 44 | openssl-dev \ 45 | && mkdir -p /app \ 46 | && curl -fsSL "https://github.com/sabnzbd/sabnzbd/releases/download/${VERSION}/SABnzbd-${VERSION}-src.tar.gz" \ 47 | | tar xzf - -C /app --strip-components=1 \ 48 | && python tools/make_mo.py \ 49 | && pip install uv \ 50 | && uv pip install --requirement /app/requirements.txt \ 51 | && chown -R root:root /app && chmod -R 755 /app \ 52 | && pip uninstall --yes uv && apk del --purge .build-deps \ 53 | && rm -rf /root/.cache /root/.cargo /tmp/* 54 | 55 | COPY . / 56 | 57 | COPY --from=ghcr.io/linuxserver/unrar:latest /usr/bin/unrar-alpine /usr/bin/unrar 58 | 59 | USER nobody:nogroup 60 | WORKDIR /config 61 | VOLUME ["/config"] 62 | 63 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 64 | -------------------------------------------------------------------------------- /apps/sabnzbd/defaults/sabnzbd.ini: -------------------------------------------------------------------------------- 1 | __version__ = 19 2 | __encoding__ = utf-8 3 | [misc] 4 | pre_script = None 5 | queue_complete = "" 6 | queue_complete_pers = 0 7 | bandwidth_perc = 100 8 | refresh_rate = 0 9 | interface_settings = "" 10 | queue_limit = 20 11 | config_lock = 0 12 | sched_converted = 0 13 | notified_new_skin = 0 14 | direct_unpack_tested = 0 15 | check_new_rel = 1 16 | auto_browser = 0 17 | language = en 18 | enable_https_verification = 1 19 | host = 0.0.0.0 20 | port = 8080 21 | https_port = "" 22 | username = "" 23 | password = "" 24 | bandwidth_max = "" 25 | cache_limit = "" 26 | web_dir = Glitter 27 | web_color = "" 28 | https_cert = server.cert 29 | https_key = server.key 30 | https_chain = "" 31 | enable_https = 0 32 | inet_exposure = 0 33 | local_ranges = , 34 | api_key = "" 35 | nzb_key = "" 36 | permissions = "" 37 | download_dir = Downloads/incomplete 38 | download_free = "" 39 | complete_dir = Downloads/complete 40 | complete_free = "" 41 | fulldisk_autoresume = 0 42 | script_dir = "" 43 | nzb_backup_dir = "" 44 | admin_dir = admin 45 | dirscan_dir = "" 46 | dirscan_speed = 5 47 | password_file = "" 48 | log_dir = logs 49 | max_art_tries = 3 50 | load_balancing = 2 51 | top_only = 0 52 | sfv_check = 1 53 | quick_check_ext_ignore = nfo, sfv, srr 54 | script_can_fail = 0 55 | enable_recursive = 1 56 | flat_unpack = 0 57 | par_option = "" 58 | pre_check = 0 59 | nice = "" 60 | win_process_prio = 3 61 | ionice = "" 62 | fail_hopeless_jobs = 1 63 | fast_fail = 1 64 | auto_disconnect = 1 65 | no_dupes = 0 66 | no_series_dupes = 0 67 | series_propercheck = 1 68 | pause_on_pwrar = 1 69 | ignore_samples = 0 70 | deobfuscate_final_filenames = 0 71 | auto_sort = "" 72 | direct_unpack = 0 73 | direct_unpack_threads = 3 74 | propagation_delay = 0 75 | folder_rename = 1 76 | replace_spaces = 0 77 | replace_dots = 0 78 | safe_postproc = 1 79 | pause_on_post_processing = 0 80 | sanitize_safe = 0 81 | cleanup_list = , 82 | unwanted_extensions = , 83 | action_on_unwanted_extensions = 0 84 | new_nzb_on_failure = 0 85 | history_retention = 0 86 | enable_meta = 1 87 | quota_size = "" 88 | quota_day = "" 89 | quota_resume = 0 90 | quota_period = m 91 | rating_enable = 0 92 | rating_host = "" 93 | rating_api_key = "" 94 | rating_filter_enable = 0 95 | rating_filter_abort_audio = 0 96 | rating_filter_abort_video = 0 97 | rating_filter_abort_encrypted = 0 98 | rating_filter_abort_encrypted_confirm = 0 99 | rating_filter_abort_spam = 0 100 | rating_filter_abort_spam_confirm = 0 101 | rating_filter_abort_downvoted = 0 102 | rating_filter_abort_keywords = "" 103 | rating_filter_pause_audio = 0 104 | rating_filter_pause_video = 0 105 | rating_filter_pause_encrypted = 0 106 | rating_filter_pause_encrypted_confirm = 0 107 | rating_filter_pause_spam = 0 108 | rating_filter_pause_spam_confirm = 0 109 | rating_filter_pause_downvoted = 0 110 | rating_filter_pause_keywords = "" 111 | enable_tv_sorting = 0 112 | tv_sort_string = "" 113 | tv_sort_countries = 1 114 | tv_categories = "" 115 | enable_movie_sorting = 0 116 | movie_sort_string = "" 117 | movie_sort_extra = -cd%1 118 | movie_extra_folder = 0 119 | movie_categories = movies, 120 | enable_date_sorting = 0 121 | date_sort_string = "" 122 | date_categories = tv, 123 | schedlines = , 124 | rss_rate = 60 125 | ampm = 0 126 | replace_illegal = 1 127 | start_paused = 0 128 | enable_all_par = 0 129 | enable_par_cleanup = 1 130 | enable_unrar = 1 131 | enable_unzip = 1 132 | enable_7zip = 1 133 | enable_filejoin = 1 134 | enable_tsjoin = 1 135 | overwrite_files = 0 136 | ignore_unrar_dates = 0 137 | backup_for_duplicates = 1 138 | empty_postproc = 0 139 | wait_for_dfolder = 0 140 | rss_filenames = 0 141 | api_logging = 1 142 | html_login = 1 143 | osx_menu = 1 144 | osx_speed = 1 145 | warn_dupl_jobs = 1 146 | helpfull_warnings = 1 147 | keep_awake = 1 148 | win_menu = 1 149 | allow_incomplete_nzb = 0 150 | enable_broadcast = 1 151 | max_art_opt = 0 152 | ipv6_hosting = 0 153 | fixed_ports = 1 154 | api_warnings = 1 155 | disable_api_key = 0 156 | no_penalties = 0 157 | x_frame_options = 1 158 | require_modern_tls = 0 159 | num_decoders = 3 160 | rss_odd_titles = nzbindex.nl/, nzbindex.com/, nzbclub.com/ 161 | req_completion_rate = 100.2 162 | selftest_host = self-test.sabnzbd.org 163 | movie_rename_limit = 100M 164 | size_limit = 0 165 | show_sysload = 2 166 | history_limit = 10 167 | wait_ext_drive = 5 168 | max_foldername_length = 246 169 | nomedia_marker = "" 170 | ipv6_servers = 1 171 | url_base = /sabnzbd 172 | host_whitelist = 2b4152ea8457, 173 | max_url_retries = 10 174 | downloader_sleep_time = 10 175 | ssdp_broadcast_interval = 15 176 | email_server = "" 177 | email_to = , 178 | email_from = "" 179 | email_account = "" 180 | email_pwd = "" 181 | email_endjob = 0 182 | email_full = 0 183 | email_dir = "" 184 | email_rss = 0 185 | email_cats = *, 186 | [logging] 187 | log_level = 1 188 | max_log_size = 5242880 189 | log_backups = 5 190 | [ncenter] 191 | ncenter_enable = 0 192 | ncenter_cats = *, 193 | ncenter_prio_startup = 1 194 | ncenter_prio_download = 0 195 | ncenter_prio_pause_resume = 0 196 | ncenter_prio_pp = 0 197 | ncenter_prio_complete = 1 198 | ncenter_prio_failed = 1 199 | ncenter_prio_disk_full = 1 200 | ncenter_prio_new_login = 0 201 | ncenter_prio_warning = 0 202 | ncenter_prio_error = 0 203 | ncenter_prio_queue_done = 1 204 | ncenter_prio_other = 1 205 | [acenter] 206 | acenter_enable = 0 207 | acenter_cats = *, 208 | acenter_prio_startup = 0 209 | acenter_prio_download = 0 210 | acenter_prio_pause_resume = 0 211 | acenter_prio_pp = 0 212 | acenter_prio_complete = 1 213 | acenter_prio_failed = 1 214 | acenter_prio_disk_full = 1 215 | acenter_prio_new_login = 0 216 | acenter_prio_warning = 0 217 | acenter_prio_error = 0 218 | acenter_prio_queue_done = 1 219 | acenter_prio_other = 1 220 | [ntfosd] 221 | ntfosd_enable = 1 222 | ntfosd_cats = *, 223 | ntfosd_prio_startup = 1 224 | ntfosd_prio_download = 0 225 | ntfosd_prio_pause_resume = 0 226 | ntfosd_prio_pp = 0 227 | ntfosd_prio_complete = 1 228 | ntfosd_prio_failed = 1 229 | ntfosd_prio_disk_full = 1 230 | ntfosd_prio_new_login = 0 231 | ntfosd_prio_warning = 0 232 | ntfosd_prio_error = 0 233 | ntfosd_prio_queue_done = 1 234 | ntfosd_prio_other = 1 235 | [prowl] 236 | prowl_enable = 0 237 | prowl_cats = *, 238 | prowl_apikey = "" 239 | prowl_prio_startup = -3 240 | prowl_prio_download = -3 241 | prowl_prio_pause_resume = -3 242 | prowl_prio_pp = -3 243 | prowl_prio_complete = 0 244 | prowl_prio_failed = 1 245 | prowl_prio_disk_full = 1 246 | prowl_prio_new_login = -3 247 | prowl_prio_warning = -3 248 | prowl_prio_error = -3 249 | prowl_prio_queue_done = 0 250 | prowl_prio_other = 0 251 | [pushover] 252 | pushover_token = "" 253 | pushover_userkey = "" 254 | pushover_device = "" 255 | pushover_emergency_expire = 3600 256 | pushover_emergency_retry = 60 257 | pushover_enable = 0 258 | pushover_cats = *, 259 | pushover_prio_startup = -3 260 | pushover_prio_download = -2 261 | pushover_prio_pause_resume = -2 262 | pushover_prio_pp = -3 263 | pushover_prio_complete = -1 264 | pushover_prio_failed = -1 265 | pushover_prio_disk_full = 1 266 | pushover_prio_new_login = -3 267 | pushover_prio_warning = 1 268 | pushover_prio_error = 1 269 | pushover_prio_queue_done = -1 270 | pushover_prio_other = -1 271 | [pushbullet] 272 | pushbullet_enable = 0 273 | pushbullet_cats = *, 274 | pushbullet_apikey = "" 275 | pushbullet_device = "" 276 | pushbullet_prio_startup = 0 277 | pushbullet_prio_download = 0 278 | pushbullet_prio_pause_resume = 0 279 | pushbullet_prio_pp = 0 280 | pushbullet_prio_complete = 1 281 | pushbullet_prio_failed = 1 282 | pushbullet_prio_disk_full = 1 283 | pushbullet_prio_new_login = 0 284 | pushbullet_prio_warning = 0 285 | pushbullet_prio_error = 0 286 | pushbullet_prio_queue_done = 0 287 | pushbullet_prio_other = 1 288 | [nscript] 289 | nscript_enable = 0 290 | nscript_cats = *, 291 | nscript_script = "" 292 | nscript_parameters = "" 293 | nscript_prio_startup = 1 294 | nscript_prio_download = 0 295 | nscript_prio_pause_resume = 0 296 | nscript_prio_pp = 0 297 | nscript_prio_complete = 1 298 | nscript_prio_failed = 1 299 | nscript_prio_disk_full = 1 300 | nscript_prio_new_login = 0 301 | nscript_prio_warning = 0 302 | nscript_prio_error = 0 303 | nscript_prio_queue_done = 1 304 | nscript_prio_other = 1 305 | -------------------------------------------------------------------------------- /apps/sabnzbd/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "sabnzbd" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=sabnzbd/sabnzbd 9 | default = "4.5.1" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/sabnzbd/sabnzbd" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/sabnzbd/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ ! -f "/config/sabnzbd.ini" ]]; then 4 | printf "Copying over default configuration ...\n" 5 | mkdir -p /config/sabnzbd 6 | cp /defaults/sabnzbd.ini /config/sabnzbd.ini 7 | 8 | printf "Creating api keys ...\n" 9 | api_key=$(tr -dc 'a-z0-9' < /dev/urandom | fold -w 32 | head -n 1) 10 | nzb_key=$(tr -dc 'a-z0-9' < /dev/urandom | fold -w 32 | head -n 1) 11 | sed -i -e "s|^api_key *=.*$|api_key = ${api_key}|g" /config/sabnzbd.ini 12 | sed -i -e "s|^nzb_key *=.*$|nzb_key = ${nzb_key}|g" /config/sabnzbd.ini 13 | fi 14 | 15 | [[ -n "${SABNZBD__API_KEY}" ]] && sed -i -e "s|^api_key *=.*$|api_key = ${SABNZBD__API_KEY}|g" /config/sabnzbd.ini 16 | [[ -n "${SABNZBD__NZB_KEY}" ]] && sed -i -e "s|^nzb_key *=.*$|nzb_key = ${SABNZBD__NZB_KEY}|g" /config/sabnzbd.ini 17 | [[ -n "${SABNZBD__HOST_WHITELIST_ENTRIES}" ]] && sed -i -e "s|^host_whitelist *=.*$|host_whitelist = ${HOSTNAME:-sabnzbd}, ${SABNZBD__HOST_WHITELIST_ENTRIES}|g" /config/sabnzbd.ini 18 | [[ -n "${SABNZBD__LOCAL_RANGES_ENTRIES}" ]] && sed -i -e "s|^local_ranges *=.*$|local_ranges = ${SABNZBD__LOCAL_RANGES_ENTRIES}|g" /config/sabnzbd.ini 19 | 20 | exec \ 21 | /usr/local/bin/python \ 22 | /app/SABnzbd.py \ 23 | --browser 0 \ 24 | --server ${SABNZBD__ADDRESS}:${SABNZBD__PORT} \ 25 | --config-file /config/sabnzbd.ini \ 26 | --disable-file-log \ 27 | --console \ 28 | "$@" 29 | -------------------------------------------------------------------------------- /apps/sabnzbd/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | python: 5 | running: true 6 | port: 7 | tcp6:8080: 8 | listening: true 9 | http: 10 | http://localhost:8080/sabnzbd: 11 | status: 200 12 | file: 13 | /usr/local/bin/python: 14 | exists: true 15 | /usr/bin/unrar: 16 | exists: true 17 | /usr/bin/par2: 18 | exists: true 19 | /usr/bin/par2create: 20 | exists: true 21 | filetype: symlink 22 | /usr/bin/par2repair: 23 | exists: true 24 | filetype: symlink 25 | /usr/bin/par2verify: 26 | exists: true 27 | filetype: symlink 28 | -------------------------------------------------------------------------------- /apps/smartctl-exporter/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG VERSION 5 | ARG TARGETOS 6 | ARG TARGETARCH 7 | ARG TARGETPLATFORM 8 | 9 | RUN apk add --no-cache smartmontools \ 10 | && wget "https://github.com/prometheus-community/smartctl_exporter/releases/download/v$VERSION/smartctl_exporter-$VERSION.$TARGETOS-$TARGETARCH.tar.gz" \ 11 | && tar xvzf smartctl_exporter-$VERSION.$TARGETOS-$TARGETARCH.tar.gz \ 12 | && ls -l smartctl_exporter-$VERSION.$TARGETOS-$TARGETARCH.tar.gz \ 13 | && mv smartctl_exporter-$VERSION.$TARGETOS-$TARGETARCH/smartctl_exporter /bin/smartctl_exporter \ 14 | && chmod +x /bin/smartctl_exporter \ 15 | && rm -rf smartctl_exporter* 16 | 17 | USER nobody 18 | ENTRYPOINT ["/bin/smartctl_exporter"] 19 | -------------------------------------------------------------------------------- /apps/smartctl-exporter/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "smartctl-exporter" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=prometheus-community/smartctl_exporter 9 | default = "0.14.0" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/prometheus-community/smartctl_exporter" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/smartctl-exporter/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | file: 4 | /bin/smartctl_exporter: 5 | exists: true 6 | port: 7 | tcp6:9633: 8 | listening: true 9 | -------------------------------------------------------------------------------- /apps/sonarr/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG TARGETARCH 5 | ARG VENDOR 6 | ARG VERSION 7 | 8 | ENV DOTNET_EnableDiagnostics=0 \ 9 | SONARR__UPDATE__BRANCH=develop 10 | 11 | USER root 12 | WORKDIR /app 13 | 14 | RUN \ 15 | apk add --no-cache \ 16 | bash \ 17 | ca-certificates \ 18 | catatonit \ 19 | coreutils \ 20 | curl \ 21 | icu-libs \ 22 | jq \ 23 | libintl \ 24 | nano \ 25 | sqlite-libs \ 26 | tzdata \ 27 | && mkdir -p /app/bin \ 28 | && curl -fsSL "https://services.sonarr.tv/v1/update/${SONARR__UPDATE__BRANCH}/download?version=${VERSION}&os=linuxmusl&runtime=netcore&arch=${TARGETARCH/amd64/x64}" \ 29 | | tar xzf - -C /app/bin --strip-components=1 \ 30 | && printf "UpdateMethod=docker\nBranch=%s\nPackageVersion=%s\nPackageAuthor=[%s](https://github.com/%s)\n" "${SONARR__UPDATE__BRANCH}" "${VERSION}" "${VENDOR}" "${VENDOR}" > /app/package_info \ 31 | && chown -R root:root /app && chmod -R 755 /app \ 32 | && rm -rf /tmp/* /app/bin/Sonarr.Update 33 | 34 | COPY . / 35 | 36 | USER nobody:nogroup 37 | WORKDIR /config 38 | VOLUME ["/config"] 39 | 40 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 41 | -------------------------------------------------------------------------------- /apps/sonarr/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "sonarr" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=custom.sonarr-develop depName=sonarr versioning=loose 9 | default = "4.0.14.2938" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/Sonarr/Sonarr" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/sonarr/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec \ 4 | /app/bin/Sonarr \ 5 | --nobrowser \ 6 | --data=/config \ 7 | "$@" 8 | -------------------------------------------------------------------------------- /apps/sonarr/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | Sonarr: 5 | running: true 6 | port: 7 | tcp6:8989: 8 | listening: true 9 | http: 10 | http://localhost:8989: 11 | status: 200 12 | -------------------------------------------------------------------------------- /apps/tautulli/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/python:3.13-alpine3.22 4 | ARG VERSION 5 | 6 | ENV \ 7 | CRYPTOGRAPHY_DONT_BUILD_RUST=1 \ 8 | PIP_BREAK_SYSTEM_PACKAGES=1 \ 9 | PIP_DISABLE_PIP_VERSION_CHECK=1 \ 10 | PIP_NO_CACHE_DIR=1 \ 11 | PIP_ROOT_USER_ACTION=ignore \ 12 | PYTHONDONTWRITEBYTECODE=1 \ 13 | PYTHONUNBUFFERED=1 \ 14 | UV_NO_CACHE=true \ 15 | UV_SYSTEM_PYTHON=true \ 16 | UV_EXTRA_INDEX_URL="https://wheel-index.linuxserver.io/alpine-3.22/" 17 | 18 | ENV TAUTULLI_DOCKER="True" \ 19 | TAUTULLI__PORT=8181 20 | 21 | USER root 22 | WORKDIR /app 23 | 24 | RUN \ 25 | apk add --no-cache \ 26 | bash \ 27 | ca-certificates \ 28 | catatonit \ 29 | coreutils \ 30 | curl \ 31 | jq \ 32 | nano \ 33 | tzdata \ 34 | && \ 35 | apk add --no-cache --virtual .build-deps \ 36 | build-base \ 37 | cargo \ 38 | libffi-dev \ 39 | musl-dev \ 40 | openssl-dev \ 41 | && pip install uv \ 42 | && uv pip install \ 43 | --requirement "https://raw.githubusercontent.com/Tautulli/tautulli-baseimage/python3/requirements.txt" \ 44 | && mkdir -p /app \ 45 | && curl -fsSL "https://github.com/Tautulli/Tautulli/archive/v${VERSION}.tar.gz" \ 46 | | tar xzf - -C /app --strip-components 1 \ 47 | && echo "v${VERSION}" > /app/version.txt \ 48 | && echo "master" > /app/branch.txt \ 49 | && chown -R root:root /app && chmod -R 755 /app \ 50 | && pip uninstall --yes uv && apk del --purge .build-deps \ 51 | && rm -rf /root/.cache /root/.cargo /tmp/* 52 | 53 | COPY . / 54 | 55 | USER nobody:nogroup 56 | WORKDIR /config 57 | VOLUME ["/config"] 58 | 59 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 60 | -------------------------------------------------------------------------------- /apps/tautulli/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "tautulli" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=Tautulli/Tautulli 9 | default = "2.15.2" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/Tautulli/Tautulli" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/tautulli/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec \ 4 | /usr/local/bin/python \ 5 | /app/Tautulli.py \ 6 | --nolaunch \ 7 | --port ${TAUTULLI__PORT} \ 8 | --config /config/config.ini \ 9 | --datadir /config \ 10 | "$@" 11 | -------------------------------------------------------------------------------- /apps/tautulli/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | python: 5 | running: true 6 | port: 7 | tcp:8181: 8 | listening: true 9 | http: 10 | http://localhost:8181: 11 | status: 200 12 | file: 13 | /usr/local/bin/python: 14 | exists: true 15 | -------------------------------------------------------------------------------- /apps/theme-park/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/python:3.13-alpine3.22 AS builder 4 | ARG VERSION 5 | RUN \ 6 | apk add --no-cache \ 7 | curl \ 8 | grep \ 9 | sed \ 10 | tar \ 11 | && \ 12 | curl -fsSL "https://github.com/themepark-dev/theme.park/archive/${VERSION}.tar.gz" \ 13 | | tar xzf - -C /tmp --strip-components 1 \ 14 | && python /tmp/themes.py \ 15 | && grep -rl 'https://theme-park.dev' /tmp | xargs sed -i 's|https\://theme-park.dev||g' 16 | 17 | FROM ghcr.io/nginxinc/nginx-unprivileged:1.27-alpine 18 | ENV NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE=1 19 | COPY --from=builder --chown=nginx:nginx /tmp/index.html /usr/share/nginx/html/ 20 | COPY --from=builder --chown=nginx:nginx /tmp/css /usr/share/nginx/html/css/ 21 | COPY --from=builder --chown=nginx:nginx /tmp/resources /usr/share/nginx/html/resources/ 22 | USER nginx 23 | WORKDIR /usr/share/nginx/html 24 | -------------------------------------------------------------------------------- /apps/theme-park/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "theme-park" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=themepark-dev/theme.park 9 | default = "1.20.1" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/themepark-dev/theme.park" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/theme-park/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | nginx: 5 | running: true 6 | port: 7 | tcp:8080: 8 | listening: true 9 | http: 10 | http://localhost:8080: 11 | status: 200 12 | -------------------------------------------------------------------------------- /apps/transmission/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | # NOTE: Alpine version is tied to the version in the Renovate annotation in docker-bake.hcl 4 | FROM docker.io/library/python:3.13-alpine3.22 5 | ARG TARGETARCH 6 | ARG MINIJINJA_ARCH=${TARGETARCH/arm64/aarch64} 7 | ARG MINIJINJA_ARCH=${MINIJINJA_ARCH/amd64/x86_64} 8 | ARG VERSION 9 | 10 | ENV HOME="/config" \ 11 | XDG_CONFIG_HOME="/config" \ 12 | XDG_DATA_HOME="/config" \ 13 | TRANSMISSION_WEB_HOME="/usr/share/transmission/public_html" 14 | 15 | RUN \ 16 | apk add --no-cache \ 17 | 7zip \ 18 | bash \ 19 | ca-certificates \ 20 | catatonit \ 21 | coreutils \ 22 | curl \ 23 | jq \ 24 | nano \ 25 | transmission-daemon=="${VERSION}" \ 26 | transmission-cli=="${VERSION}" \ 27 | transmission-extra=="${VERSION}" \ 28 | transmission-remote=="${VERSION}" \ 29 | tzdata \ 30 | && \ 31 | curl -fsSL https://github.com/mitsuhiko/minijinja/releases/download/2.8.0/minijinja-cli-${MINIJINJA_ARCH}-unknown-linux-musl.tar.xz \ 32 | | tar xJf - -C /usr/local/bin/ --strip-components=1 \ 33 | && \ 34 | rm -rf /tmp/* 35 | 36 | COPY . / 37 | 38 | COPY --from=ghcr.io/linuxserver/unrar:latest /usr/bin/unrar-alpine /usr/bin/unrar 39 | 40 | USER nobody:nogroup 41 | WORKDIR /config 42 | VOLUME ["/config"] 43 | 44 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 45 | -------------------------------------------------------------------------------- /apps/transmission/defaults/settings.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "alt-speed-down": {{ ENV.TRANSMISSION__ALT_SPEED_DOWN | default(50) }}, 3 | "alt-speed-enabled": {{ ENV.TRANSMISSION__ALT_SPEED_ENABLED | default(false) }}, 4 | "alt-speed-time-begin": {{ ENV.TRANSMISSION__ALT_SPEED_TIME_BEGIN | default(540) }}, 5 | "alt-speed-time-day": {{ ENV.TRANSMISSION__ALT_SPEED_TIME_DAY | default(127) }}, 6 | "alt-speed-time-enabled": {{ ENV.TRANSMISSION__ALT_SPEED_TIME_ENABLED | default(false) }}, 7 | "alt-speed-time-end": {{ ENV.TRANSMISSION__ALT_SPEED_TIME_END | default(1020) }}, 8 | "alt-speed-up": {{ ENV.TRANSMISSION__ALT_SPEED_UP | default(50) }}, 9 | "announce-ip": {{ ENV.TRANSMISSION__ANNOUNCE_IP }}, 10 | "announce-ip-enabled": {{ ENV.TRANSMISSION__ANNOUNCE_IP_ENABLED | default(false) }}, 11 | "anti-brute-force-enabled": {{ ENV.TRANSMISSION__ANTI_BRUTE_FORCE_ENABLED | default(false) }}, 12 | "anti-brute-force-threshold": {{ ENV.TRANSMISSION__ANTI_BRUTE_FORCE_THRESHOLD | default(100) }}, 13 | "bind-address-ipv4": {{ ENV.TRANSMISSION__BIND_ADDRESS_IPV4 | default('0.0.0.0') }}, 14 | "bind-address-ipv6": {{ ENV.TRANSMISSION__BIND_ADDRESS_IPV6 | default('::') }}, 15 | "blocklist-enabled": {{ ENV.TRANSMISSION__BLOCKLIST_ENABLED | default(false) }}, 16 | "blocklist-url": {{ ENV.TRANSMISSION__BLOCKLIST_URL }}, 17 | "cache-size-mb": {{ ENV.TRANSMISSION__CACHE_SIZE_MB | default(4) }}, 18 | "default-trackers": {{ ENV.TRANSMISSION__DEFAULT_TRACKERS }}, 19 | "dht-enabled": {{ ENV.TRANSMISSION__DHT_ENABLED | default(true) }}, 20 | "download-dir": {{ ENV.TRANSMISSION__DOWNLOAD_DIR | default('/downloads/complete') }}, 21 | "download-queue-enabled": {{ ENV.TRANSMISSION__DOWNLOAD_QUEUE_ENABLED | default(true) }}, 22 | "download-queue-size": {{ ENV.TRANSMISSION__DOWNLOAD_QUEUE_SIZE | default(5) }}, 23 | "encryption": {{ ENV.TRANSMISSION__ENCRYPTION | default(1) }}, 24 | "idle-seeding-limit": {{ ENV.TRANSMISSION__IDLE_SEEDING_LIMIT | default(30) }}, 25 | "idle-seeding-limit-enabled": {{ ENV.TRANSMISSION__IDLE_SEEDING_LIMIT_ENABLED | default(false) }}, 26 | "incomplete-dir": {{ ENV.TRANSMISSION__INCOMPLETE_DIR | default('/downloads/incomplete') }}, 27 | "incomplete-dir-enabled": {{ ENV.TRANSMISSION__INCOMPLETE_DIR_ENABLED | default(true) }}, 28 | "lpd-enabled": {{ ENV.TRANSMISSION__LPD_ENABLED | default(false) }}, 29 | "message-level": {{ ENV.TRANSMISSION__MESSAGE_LEVEL | default(2) }}, 30 | "peer-congestion-algorithm": {{ ENV.TRANSMISSION__PEER_CONGESTION_ALGORITHM }}, 31 | "peer-id-ttl-hours": {{ ENV.TRANSMISSION__PEER_ID_TTL_HOURS | default(6) }}, 32 | "peer-limit-global": {{ ENV.TRANSMISSION__PEER_LIMIT_GLOBAL | default(200) }}, 33 | "peer-limit-per-torrent": {{ ENV.TRANSMISSION__PEER_LIMIT_PER_TORRENT | default(50) }}, 34 | "peer-port": {{ ENV.TRANSMISSION__PEER_PORT | default(51413) }}, 35 | "peer-port-random-high": {{ ENV.TRANSMISSION__PEER_PORT_RANDOM_HIGH | default(65535) }}, 36 | "peer-port-random-low": {{ ENV.TRANSMISSION__PEER_PORT_RANDOM_LOW | default(49152) }}, 37 | "peer-port-random-on-start": {{ ENV.TRANSMISSION__PEER_PORT_RANDOM_ON_START | default(false) }}, 38 | "peer-socket-tos": {{ ENV.TRANSMISSION__PEER_SOCKET_TOS | default('le') }}, 39 | "pex-enabled": {{ ENV.TRANSMISSION__PEX_ENABLED | default(true) }}, 40 | "port-forwarding-enabled": {{ ENV.TRANSMISSION__PORT_FORWARDING_ENABLED | default(false) }}, 41 | "preallocation": {{ ENV.TRANSMISSION__PREALLOCATION | default(1) }}, 42 | "prefetch-enabled": {{ ENV.TRANSMISSION__PREFETCH_ENABLED | default(true) }}, 43 | "queue-stalled-enabled": {{ ENV.TRANSMISSION__QUEUE_STALLED_ENABLED | default(true) }}, 44 | "queue-stalled-minutes": {{ ENV.TRANSMISSION__QUEUE_STALLED_MINUTES | default(30) }}, 45 | "ratio-limit": {{ ENV.TRANSMISSION__RATIO_LIMIT | default(2) }}, 46 | "ratio-limit-enabled": {{ ENV.TRANSMISSION__RATIO_LIMIT_ENABLED | default(false) }}, 47 | "rename-partial-files": {{ ENV.TRANSMISSION__RENAME_PARTIAL_FILES | default(true) }}, 48 | "rpc-authentication-required": {{ ENV.TRANSMISSION__RPC_AUTHENTICATION_REQUIRED | default(false) }}, 49 | "rpc-bind-address": {{ ENV.TRANSMISSION__RPC_BIND_ADDRESS | default('0.0.0.0') }}, 50 | "rpc-enabled": {{ ENV.TRANSMISSION__RPC_ENABLED | default(true) }}, 51 | "rpc-host-whitelist": {{ ENV.TRANSMISSION__RPC_HOST_WHITELIST }}, 52 | "rpc-host-whitelist-enabled": {{ ENV.TRANSMISSION__RPC_HOST_WHITELIST_ENABLED | default(false) }}, 53 | "rpc-password": {{ ENV.TRANSMISSION__RPC_PASSWORD }}, 54 | "rpc-port": {{ ENV.TRANSMISSION__RPC_PORT | default(9091) }}, 55 | "rpc-socket-mode": {{ ENV.TRANSMISSION__RPC_SOCKET_MODE | default(0750) }}, 56 | "rpc-url": {{ ENV.TRANSMISSION__RPC_URL | default('/transmission/') }}, 57 | "rpc-username": {{ ENV.TRANSMISSION__RPC_USERNAME }}, 58 | "rpc-whitelist": {{ ENV.TRANSMISSION__RPC_WHITELIST }}, 59 | "rpc-whitelist-enabled": {{ ENV.TRANSMISSION__RPC_WHITELIST_ENABLED | default(false) }}, 60 | "scrape-paused-torrents-enabled": {{ ENV.TRANSMISSION__SCRAPE_PAUSED_TORRENTS_ENABLED | default(true) }}, 61 | "script-torrent-added-enabled": {{ ENV.TRANSMISSION__SCRIPT_TORRENT_ADDED_ENABLED | default(false) }}, 62 | "script-torrent-added-filename": {{ ENV.TRANSMISSION__SCRIPT_TORRENT_ADDED_FILENAME }}, 63 | "script-torrent-done-enabled": {{ ENV.TRANSMISSION__SCRIPT_TORRENT_DONE_ENABLED | default(false) }}, 64 | "script-torrent-done-filename": {{ ENV.TRANSMISSION__SCRIPT_TORRENT_DONE_FILENAME }}, 65 | "script-torrent-done-seeding-enabled": {{ ENV.TRANSMISSION__SCRIPT_TORRENT_DONE_SEEDING_ENABLED | default(false) }}, 66 | "script-torrent-done-seeding-filename": {{ ENV.TRANSMISSION__SCRIPT_TORRENT_DONE_SEEDING_FILENAME }}, 67 | "seed-queue-enabled": {{ ENV.TRANSMISSION__SEED_QUEUE_ENABLED | default(false) }}, 68 | "seed-queue-size": {{ ENV.TRANSMISSION__SEED_QUEUE_SIZE | default(10) }}, 69 | "speed-limit-down": {{ ENV.TRANSMISSION__SPEED_LIMIT_DOWN | default(100) }}, 70 | "speed-limit-down-enabled": {{ ENV.TRANSMISSION__SPEED_LIMIT_DOWN_ENABLED | default(false) }}, 71 | "speed-limit-up": {{ ENV.TRANSMISSION__SPEED_LIMIT_UP | default(100) }}, 72 | "speed-limit-up-enabled": {{ ENV.TRANSMISSION__SPEED_LIMIT_UP_ENABLED | default(false) }}, 73 | "start-added-torrents": {{ ENV.TRANSMISSION__START_ADDED_TORRENTS | default(true) }}, 74 | "tcp-enabled": {{ ENV.TRANSMISSION__TCP_ENABLED | default(true) }}, 75 | "torrent-added-verify-mode": {{ ENV.TRANSMISSION__TORRENT_ADDED_VERIFY_MODE | default('fast') }}, 76 | "trash-original-torrent-files": {{ ENV.TRANSMISSION__TRASH_ORIGINAL_TORRENT_FILES | default(false) }}, 77 | "umask": {{ ENV.TRANSMISSION__UMASK | default(002) }}, 78 | "upload-slots-per-torrent": {{ ENV.TRANSMISSION__UPLOAD_SLOTS_PER_TORRENT | default(14) }}, 79 | "utp-enabled": {{ ENV.TRANSMISSION__UTP_ENABLED | default(true) }}, 80 | "watch-dir": {{ ENV.TRANSMISSION__WATCH_DIR | default('/watch') }}, 81 | "watch-dir-enabled": {{ ENV.TRANSMISSION__WATCH_DIR_ENABLED | default(false) }}, 82 | "watch-dir-force-generic": {{ ENV.TRANSMISSION__WATCH_FORCE_GENERIC | default(false) }} 83 | } 84 | -------------------------------------------------------------------------------- /apps/transmission/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "transmission" 5 | } 6 | 7 | variable "VERSION" { 8 | // NOTE: Alpine version is tied to the version of the base image in the Dockerfile 9 | // renovate: datasource=repology depName=alpine_3_22/transmission-daemon versioning=loose 10 | default = "4.0.6-r4" 11 | } 12 | 13 | variable "SOURCE" { 14 | default = "https://github.com/transmission/transmission" 15 | } 16 | 17 | group "default" { 18 | targets = ["image-local"] 19 | } 20 | 21 | target "image" { 22 | inherits = ["docker-metadata-action"] 23 | args = { 24 | VERSION = "${VERSION}" 25 | } 26 | labels = { 27 | "org.opencontainers.image.source" = "${SOURCE}" 28 | } 29 | } 30 | 31 | target "image-local" { 32 | inherits = ["image"] 33 | output = ["type=docker"] 34 | tags = ["${APP}:${VERSION}"] 35 | } 36 | 37 | target "image-all" { 38 | inherits = ["image"] 39 | platforms = [ 40 | "linux/amd64", 41 | "linux/arm64" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /apps/transmission/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Only template the config file if our custom env variables are present 4 | if env | grep -q "^TRANSMISSION__"; then 5 | minijinja-cli --env /defaults/settings.json.j2 > /config/settings.json 6 | fi 7 | 8 | exec \ 9 | /usr/bin/transmission-daemon \ 10 | --foreground \ 11 | --config-dir /config \ 12 | --log-level "${TRANSMISSION_LOG_LEVEL:-info}" \ 13 | "$@" 14 | -------------------------------------------------------------------------------- /apps/transmission/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | process: 3 | transmission-da: 4 | running: true 5 | port: 6 | tcp:9091: 7 | listening: true 8 | http: 9 | http://localhost:9091: 10 | status: 200 11 | file: 12 | /usr/local/bin/python: 13 | exists: true 14 | /usr/bin/unrar: 15 | exists: true 16 | -------------------------------------------------------------------------------- /apps/webhook/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/python:3.13-alpine3.22 4 | ARG TARGETARCH 5 | ARG VERSION 6 | 7 | ENV \ 8 | CRYPTOGRAPHY_DONT_BUILD_RUST=1 \ 9 | PIP_BREAK_SYSTEM_PACKAGES=1 \ 10 | PIP_DISABLE_PIP_VERSION_CHECK=1 \ 11 | PIP_NO_CACHE_DIR=1 \ 12 | PIP_ROOT_USER_ACTION=ignore \ 13 | PYTHONDONTWRITEBYTECODE=1 \ 14 | PYTHONUNBUFFERED=1 \ 15 | UV_NO_CACHE=true \ 16 | UV_SYSTEM_PYTHON=true \ 17 | UV_EXTRA_INDEX_URL="https://wheel-index.linuxserver.io/alpine-3.22/" 18 | 19 | ENV \ 20 | WEBHOOK__PORT="9000" \ 21 | WEBHOOK__URLPREFIX="hooks" 22 | 23 | USER root 24 | WORKDIR /app 25 | 26 | RUN \ 27 | apk add --no-cache \ 28 | bash \ 29 | ca-certificates \ 30 | catatonit \ 31 | coreutils \ 32 | curl \ 33 | jo \ 34 | jq \ 35 | trurl \ 36 | tzdata \ 37 | && mkdir -p /app/bin \ 38 | && \ 39 | curl -fsSL "https://github.com/adnanh/webhook/releases/download/${VERSION}/webhook-linux-${TARGETARCH}.tar.gz" \ 40 | | tar xzf - -C /app/bin --strip-components=1 \ 41 | && \ 42 | pip install uv \ 43 | && uv pip install "apprise>=1, <2" \ 44 | && chown -R root:root /app && chmod -R 755 /app \ 45 | && pip uninstall --yes uv \ 46 | && rm -rf /tmp/* 47 | 48 | COPY . / 49 | 50 | USER nobody:nogroup 51 | WORKDIR /config 52 | VOLUME ["/config"] 53 | 54 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 55 | -------------------------------------------------------------------------------- /apps/webhook/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "webhook" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=github-releases depName=adnanh/webhook 9 | default = "2.8.2" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/adnanh/webhook" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/webhook/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | HOOKS_FILE="/config/hooks.yaml" 4 | if [[ -f /config/hooks.json ]]; then 5 | HOOKS_FILE="/config/hooks.json" 6 | fi 7 | 8 | exec \ 9 | /app/bin/webhook \ 10 | -port "${WEBHOOK__PORT}" \ 11 | -urlprefix "${WEBHOOK__URLPREFIX}" \ 12 | -hooks "${HOOKS_FILE}" \ 13 | -template \ 14 | -verbose \ 15 | "$@" 16 | -------------------------------------------------------------------------------- /apps/webhook/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | webhook: 5 | running: true 6 | port: 7 | tcp6:9000: 8 | listening: true 9 | http: 10 | http://localhost:9000: 11 | status: 200 12 | -------------------------------------------------------------------------------- /apps/whisparr/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM docker.io/library/alpine:3.22 4 | ARG TARGETARCH 5 | ARG VENDOR 6 | ARG VERSION 7 | 8 | ENV COMPlus_EnableDiagnostics=0 \ 9 | WHISPARR__UPDATE__BRANCH=nightly 10 | 11 | USER root 12 | WORKDIR /app 13 | 14 | RUN \ 15 | apk add --no-cache \ 16 | bash \ 17 | ca-certificates \ 18 | catatonit \ 19 | coreutils \ 20 | curl \ 21 | icu-libs \ 22 | jq \ 23 | libintl \ 24 | nano \ 25 | sqlite-libs \ 26 | tzdata \ 27 | && mkdir -p /app/bin \ 28 | && curl -fsSL "https://whisparr.servarr.com/v1/update/${WHISPARR__UPDATE__BRANCH}/updatefile?version=${VERSION}&os=linuxmusl&runtime=netcore&arch=${TARGETARCH/amd64/x64}" \ 29 | | tar xzf - -C /app/bin --strip-components=1 \ 30 | && printf "UpdateMethod=docker\nBranch=%s\nPackageVersion=%s\nPackageAuthor=[%s](https://github.com/%s)\n" "${WHISPARR__UPDATE__BRANCH}" "${VERSION}" "${VENDOR}" "${VENDOR}" > /app/package_info \ 31 | && chown -R root:root /app && chmod -R 755 /app \ 32 | && rm -rf /tmp/* /app/bin/Whisparr.Update 33 | 34 | COPY . / 35 | 36 | USER nobody:nogroup 37 | WORKDIR /config 38 | VOLUME ["/config"] 39 | 40 | ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] 41 | 42 | LABEL org.opencontainers.image.source="https://github.com/Whisparr/Whisparr" 43 | -------------------------------------------------------------------------------- /apps/whisparr/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "docker-metadata-action" {} 2 | 3 | variable "APP" { 4 | default = "whisparr" 5 | } 6 | 7 | variable "VERSION" { 8 | // renovate: datasource=custom.servarr-nightly depName=whisparr versioning=loose 9 | default = "2.0.0.987" 10 | } 11 | 12 | variable "SOURCE" { 13 | default = "https://github.com/Whisparr/Whisparr" 14 | } 15 | 16 | group "default" { 17 | targets = ["image-local"] 18 | } 19 | 20 | target "image" { 21 | inherits = ["docker-metadata-action"] 22 | args = { 23 | VERSION = "${VERSION}" 24 | } 25 | labels = { 26 | "org.opencontainers.image.source" = "${SOURCE}" 27 | } 28 | } 29 | 30 | target "image-local" { 31 | inherits = ["image"] 32 | output = ["type=docker"] 33 | tags = ["${APP}:${VERSION}"] 34 | } 35 | 36 | target "image-all" { 37 | inherits = ["image"] 38 | platforms = [ 39 | "linux/amd64", 40 | "linux/arm64" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /apps/whisparr/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec \ 4 | /app/bin/Whisparr \ 5 | --nobrowser \ 6 | --data=/config \ 7 | "$@" 8 | -------------------------------------------------------------------------------- /apps/whisparr/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml 3 | process: 4 | Whisparr: 5 | running: true 6 | port: 7 | tcp6:6969: 8 | listening: true 9 | http: 10 | http://localhost:6969: 11 | status: 200 12 | timeout: 5000 13 | -------------------------------------------------------------------------------- /include/.dockerignore: -------------------------------------------------------------------------------- 1 | # NOTE: This file automatically copied to each applications 2 | # directory during build. Be careful not to ignore any files 3 | # that are needed in the build process. 4 | /* 5 | !defaults/ 6 | !scripts/ 7 | !entrypoint.sh 8 | --------------------------------------------------------------------------------