Automating Docker Builds with Gitea Actions
I've been using Gitea as my self-hosted Git solution for some time now, and one feature cool features is Gitea Actions. Today, I'd like to share a specific use case: automatically building and pushing Docker images whenever code is pushed to the main branch.
The workflow overview
Before diving into the implementation, let's understand what we're trying to do. We want a system that:
- Detects when we push code to our main branch
- Automatically builds a Docker image from that code
- Tags the image with both a unique identifier (for versioning) and a "latest" tag
- Pushes these images to our private Docker registry
- Does all this securely, without exposing credentials
This is a common workflow for many teams and projects, and it eliminates the tedious manual steps of building, tagging, and pushing images.
The workflow file
Here's what the complete Gitea Actions workflow looks like:
name: Build and Push Docker Image
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Registry
uses: docker/login-action@v2
with:
registry: registry.example.com
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build Docker Image
env:
BRANCH_NAME: ${{ github.ref_name }}
SHORT_HASH: ${{ github.sha }}
run: |
# Build the image with the commit hash tag
docker build --build-arg BUILD_IDENTIFIER=${SHORT_HASH:0:5} -t registry.example.com/your-username/your-repository:${BRANCH_NAME}-${SHORT_HASH:0:5} .
# Tag the same image as "latest"
docker tag registry.example.com/your-username/your-repository:${BRANCH_NAME}-${SHORT_HASH:0:5} registry.example.com/your-username/your-repository:${BRANCH_NAME}-latest
- name: Push Docker Images
env:
BRANCH_NAME: ${{ github.ref_name }}
SHORT_HASH: ${{ github.sha }}
run: |
docker push registry.example.com/your-username/your-repository:${BRANCH_NAME}-${SHORT_HASH:0:5}
docker push registry.example.com/your-username/your-repository:${BRANCH_NAME}-latest
- name: Log out from registry
if: always()
run: docker logout registry.example.com
This file needs to be saved as .gitea/workflows/docker.yml
in your repository. Let's break it down to understand how it works.
Understanding the workflow
The trigger
on:
push:
branches: [ main ]
This section defines when our workflow runs. In this case, whenever code is pushed to the main branch.
The job steps
Our workflow consists of a single job called "build" with several steps. Each step performs a specific task:
1. Checkout the code
- uses: actions/checkout@v4
This step fetches your repository's code so that the next steps can access it.
2. Login to the Docker registry
- name: Login to Registry
uses: docker/login-action@v2
with:
registry: registry.example.com
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
Here, we authenticate with our private Docker registry. Note the use of secrets. This is crucial for security. Instead of hardcoding credentials, we reference secrets that are stored securely in the Gitea repository settings.
3. Build the Docker image
- name: Build Docker Image
env:
BRANCH_NAME: ${{ github.ref_name }}
SHORT_HASH: ${{ github.sha }}
run: |
# Build the image with the commit hash tag
docker build --build-arg BUILD_IDENTIFIER=${SHORT_HASH:0:5} -t registry.example.com/your-username/your-repository:${BRANCH_NAME}-${SHORT_HASH:0:5} .
# Tag the same image as "latest"
docker tag registry.example.com/your-username/your-repository:${BRANCH_NAME}-${SHORT_HASH:0:5} registry.example.com/your-username/your-repository:${BRANCH_NAME}-latest
This step does the actual Docker image building. There are several interesting aspects here:
- We use environment variables to access the branch name and commit hash
- We truncate the commit hash to the first 5 characters for readability
- We build the image with a unique tag combining the branch name and commit hash
- We also create a "latest" tag that will always point to the most recent build
This tagging strategy gives us the best of both worlds: unique identifiers for versioning and a consistent "latest" tag for convenience.
4. Push the images to the registry
- name: Push Docker Images
env:
BRANCH_NAME: ${{ github.ref_name }}
SHORT_HASH: ${{ github.sha }}
run: |
docker push registry.example.com/your-username/your-repository:${BRANCH_NAME}-${SHORT_HASH:0:5}
docker push registry.example.com/your-username/your-repository:${BRANCH_NAME}-latest
After building, we push both versions of our tagged image to the registry.
5. Logout from the registry
- name: Log out from registry
if: always()
run: docker logout registry.example.com
As a security best practice, we log out from the registry when we're done. The if: always()
condition ensures this step runs even if previous steps failed, which is important for good security.
Setting up the repository
Before this workflow can run successfully, you'll need to configure a few things:
- Create the necessary secrets in your Gitea repository:
- Go to Repository Settings > Secrets
- Add
REGISTRY_USER
andREGISTRY_PASSWORD
with your Docker registry credentials
-
Ensure your repository has a valid Dockerfile in its root directory
-
Make sure the
.gitea/workflows
directory exists and contains thedocker.yml
file with the workflow configuration
The benefits
Implementing this workflow has several advantages:
-
Consistency: Every build follows the same process, eliminating human error from manual builds.
-
Traceability: Each image is tagged with the branch name and commit hash, making it easy to trace back from a Docker image to the exact code that created it.
-
Convenience: The "latest" tag always points to the most recent build, simplifying deployment scripts.
-
Time-saving: No more manual build and push commands - everything happens automatically when you push to main.
Customizing for your needs
This workflow is just a starting point. You can adapt it to your specific requirements:
- Change the registry URL to your own Docker registry
- Adjust the image name and tagging scheme
- Add additional build arguments or environment variables
- Modify the trigger conditions to run on different branches or events
- Add testing steps before building