{"meta":{"title":"Creating a Docker container action","intro":"In this tutorial, you'll learn how to build a Docker container action.","product":"GitHub Actions","breadcrumbs":[{"href":"/en/actions","title":"GitHub Actions"},{"href":"/en/actions/tutorials","title":"Tutorials"},{"href":"/en/actions/tutorials/use-containerized-services","title":"Use containerized services"},{"href":"/en/actions/tutorials/use-containerized-services/create-a-docker-container-action","title":"Create a Docker container action"}],"documentType":"article"},"body":"# Creating a Docker container action\n\nIn this tutorial, you'll learn how to build a Docker container action.\n\n## Introduction\n\nIn this guide, you'll learn about the basic components needed to create and use a packaged Docker container action. To focus this guide on the components needed to package the action, the functionality of the action's code is minimal. The action prints \"Hello World\" in the logs or \"Hello \\[who-to-greet]\" if you provide a custom name.\n\nOnce you complete this project, you should understand how to build your own Docker container action and test it in a workflow.\n\nSelf-hosted runners must use a Linux operating system and have Docker installed to run Docker container actions. For more information about the requirements of self-hosted runners, see [Self-hosted runners](/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#requirements-for-self-hosted-runner-machines).\n\n> \\[!WARNING]\n> When creating workflows and actions, you should always consider whether your code might execute untrusted input from possible attackers. Certain contexts should be treated as untrusted input, as an attacker could insert their own malicious content. For more information, see [Secure use reference](/en/actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections).\n\n## Prerequisites\n\n* You must create a repository on GitHub and clone it to your workstation. For more information, see [Creating a new repository](/en/repositories/creating-and-managing-repositories/creating-a-new-repository) and [Cloning a repository](/en/repositories/creating-and-managing-repositories/cloning-a-repository).\n* If your repository uses Git LFS, you must include the objects in archives of your repository. For more information, see [Managing Git LFS objects in archives of your repository](/en/enterprise-cloud@latest/repositories/managing-your-repositorys-settings-and-features/managing-repository-settings/managing-git-lfs-objects-in-archives-of-your-repository).\n* You may find it helpful to have a basic understanding of GitHub Actions, environment variables and the Docker container filesystem. For more information, see [Store information in variables](/en/actions/learn-github-actions/variables) and [GitHub-hosted runners](/en/enterprise-cloud@latest/actions/using-github-hosted-runners/about-github-hosted-runners#docker-container-filesystem).\n\n## Creating a Dockerfile\n\nIn your new `hello-world-docker-action` directory, create a new `Dockerfile` file. Make sure that your filename is capitalized correctly (use a capital `D` but not a capital `f`) if you're having issues. For more information, see [Dockerfile support for GitHub Actions](/en/actions/creating-actions/dockerfile-support-for-github-actions).\n\n**Dockerfile**\n\n```dockerfile copy\n# Container image that runs your code\nFROM alpine:3.10\n\n# Copies your code file from your action repository to the filesystem path `/` of the container\nCOPY entrypoint.sh /entrypoint.sh\n\n# Code file to execute when the docker container starts up (`entrypoint.sh`)\nENTRYPOINT [\"/entrypoint.sh\"]\n```\n\n## Creating an action metadata file\n\nCreate a new `action.yml` file in the `hello-world-docker-action` directory you created above. For more information, see [Metadata syntax reference](/en/actions/creating-actions/metadata-syntax-for-github-actions).\n\n**action.yml**\n\n```yaml copy\n# action.yml\nname: 'Hello World'\ndescription: 'Greet someone and record the time'\ninputs:\n  who-to-greet:  # id of input\n    description: 'Who to greet'\n    required: true\n    default: 'World'\noutputs:\n  time: # id of output\n    description: 'The time we greeted you'\nruns:\n  using: 'docker'\n  image: 'Dockerfile'\n  args:\n    - ${{ inputs.who-to-greet }}\n```\n\nThis metadata defines one `who-to-greet` input and one `time` output parameter. To pass inputs to the Docker container, you should declare the input using `inputs` and pass the input in the `args` keyword. Everything you include in `args` is passed to the container, but for better discoverability for users of your action, we recommended using inputs.\n\nGitHub will build an image from your `Dockerfile`, and run commands in a new container using this image.\n\n## Writing the action code\n\nYou can choose any base Docker image and, therefore, any language for your action. The following shell script example uses the `who-to-greet` input variable to print \"Hello \\[who-to-greet]\" in the log file.\n\nNext, the script gets the current time and sets it as an output variable that actions running later in a job can use. In order for GitHub to recognize output variables, you must write them to the `$GITHUB_OUTPUT` environment file: `echo \"<output name>=<value>\" >> $GITHUB_OUTPUT`. For more information, see [Workflow commands for GitHub Actions](/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter).\n\n1. Create a new `entrypoint.sh` file in the `hello-world-docker-action` directory.\n\n2. Add the following code to your `entrypoint.sh` file.\n\n   **entrypoint.sh**\n\n   ```shell copy\n   #!/bin/sh -l\n\n   echo \"Hello $1\"\n   time=$(date)\n   echo \"time=$time\" >> $GITHUB_OUTPUT\n\n   ```\n\n   If `entrypoint.sh` executes without any errors, the action's status is set to `success`. You can also explicitly set exit codes in your action's code to provide an action's status. For more information, see [Setting exit codes for actions](/en/actions/creating-actions/setting-exit-codes-for-actions).\n\n3. Make your `entrypoint.sh` file executable. Git provides a way to explicitly change the permission mode of a file so that it doesn’t get reset every time there is a clone/fork.\n\n   ```shell copy\n   git add entrypoint.sh\n   git update-index --chmod=+x entrypoint.sh\n   ```\n\n4. Optionally, to check the permission mode of the file in the git index, run the following command.\n\n   ```shell copy\n   git ls-files --stage entrypoint.sh\n   ```\n\n   An output like `100755 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       entrypoint.sh` means the file has the executable permission. In this example, `755` denotes the executable permission.\n\n## Creating a README\n\nTo let people know how to use your action, you can create a README file. A README is most helpful when you plan to share your action publicly, but is also a great way to remind you or your team how to use the action.\n\nIn your `hello-world-docker-action` directory, create a `README.md` file that specifies the following information:\n\n* A detailed description of what the action does.\n* Required input and output arguments.\n* Optional input and output arguments.\n* Secrets the action uses.\n* Environment variables the action uses.\n* An example of how to use your action in a workflow.\n\n**README.md**\n\n```markdown copy\n# Hello world docker action\n\nThis action prints \"Hello World\" or \"Hello\" + the name of a person to greet to the log.\n\n## Inputs\n\n## `who-to-greet`\n\n**Required** The name of the person to greet. Default `\"World\"`.\n\n## Outputs\n\n## `time`\n\nThe time we greeted you.\n\n## Example usage\n\nuses: actions/hello-world-docker-action@v2\nwith:\n  who-to-greet: 'Mona the Octocat'\n```\n\n## Commit, tag, and push your action\n\nFrom your terminal, commit your `action.yml`, `entrypoint.sh`, `Dockerfile`, and `README.md` files.\n\nIt's best practice to also add a version tag for releases of your action. For more information on versioning your action, see [About custom actions](/en/actions/creating-actions/about-custom-actions#using-release-management-for-actions).\n\n```shell copy\ngit add action.yml entrypoint.sh Dockerfile README.md\ngit commit -m \"My first action is ready\"\ngit tag -a -m \"My first action release\" v1\ngit push --follow-tags\n```\n\n## Testing out your action in a workflow\n\nNow you're ready to test your action out in a workflow.\n\n* When an action is in a private repository, you can control who can access it. For more information, see [Managing GitHub Actions settings for a repository](/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#allowing-access-to-components-in-a-private-repository).\n* When an action is in an internal repository, the action can only be used in workflows in the same repository.\n* Public actions can be used by workflows in any repository.\n\n### Example using a public action\n\nThe following workflow code uses the completed *hello world* action in the public [`actions/hello-world-docker-action`](https://github.com/actions/hello-world-docker-action) repository. Copy the following workflow example code into a `.github/workflows/main.yml` file, but replace the `actions/hello-world-docker-action` with your repository and action name. You can also replace the `who-to-greet` input with your name. Public actions can be used even if they're not published to GitHub Marketplace. For more information, see [Publishing actions in GitHub Marketplace](/en/actions/creating-actions/publishing-actions-in-github-marketplace#publishing-an-action).\n\n**.github/workflows/main.yml**\n\n```yaml copy\non: [push]\n\njobs:\n  hello_world_job:\n    runs-on: ubuntu-latest\n    name: A job to say hello\n    steps:\n      - name: Hello world action step\n        id: hello\n        uses: actions/hello-world-docker-action@v2\n        with:\n          who-to-greet: 'Mona the Octocat'\n      # Use the output from the `hello` step\n      - name: Get the output time\n        run: echo \"The time was ${{ steps.hello.outputs.time }}\"\n```\n\n### Example using a private action\n\nCopy the following example workflow code into a `.github/workflows/main.yml` file in your action's repository. You can also replace the `who-to-greet` input with your name. This private action can't be published to GitHub Marketplace, and can only be used in this repository.\n\n**.github/workflows/main.yml**\n\n```yaml copy\non: [push]\n\njobs:\n  hello_world_job:\n    runs-on: ubuntu-latest\n    name: A job to say hello\n    steps:\n      # To use this repository's private action,\n      # you must check out the repository\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Hello world action step\n        uses: ./ # Uses an action in the root directory\n        id: hello\n        with:\n          who-to-greet: 'Mona the Octocat'\n      # Use the output from the `hello` step\n      - name: Get the output time\n        run: echo \"The time was ${{ steps.hello.outputs.time }}\"\n```\n\nFrom your repository, click the **Actions** tab, and select the latest workflow run. Under **Jobs** or in the visualization graph, click **A job to say hello**.\n\nClick **Hello world action step**, and you should see \"Hello Mona the Octocat\" or the name you used for the `who-to-greet` input printed in the log. To see the timestamp, click **Get the output time**.\n\n## Accessing files created by a container action\n\nWhen a container action runs, it will automatically map the default working directory (`GITHUB_WORKSPACE`) on the runner with the `/github/workspace` directory on the container. Any files added to this directory on the container will be available to any subsequent steps in the same job. For example, if you have a container action that builds your project, and you would like to upload the build output as an artifact, you can use the following steps.\n\n**workflow\\.yml**\n\n```yaml copy\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      # Output build artifacts to /github/workspace on the container.\n      - name: Containerized Build\n        uses: ./.github/actions/my-container-action\n\n      - name: Upload Build Artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: workspace_artifacts\n          path: ${{ github.workspace }}\n```\n\nFor more information about uploading build output as an artifact, see [Store and share data with workflow artifacts](/en/actions/using-workflows/storing-workflow-data-as-artifacts).\n\n## Example Docker container actions on GitHub.com\n\nYou can find many examples of Docker container actions on GitHub.com.\n\n* [github/issue-metrics](https://github.com/github/issue-metrics)\n* [microsoft/infersharpaction](https://github.com/microsoft/infersharpaction)\n* [microsoft/ps-docs](https://github.com/microsoft/ps-docs)"}