GitLab allows it, to execute commands after repository commits. With that, it’s possible to do automated software tests for code in the repository.

A few weeks ago, a private project required automatic releases on GitLab at given dates – for example monthly. At the moment such a feature is not (yet) supported by GitLab (

However, it is possible to create releases via the GitLab API. So it is just necessary, to use this API, to allow a release in the CI/CD Process. For creating releases via this API there’s already a working Docker-Image which we will use. (

So what do we need to do, to get things done? How can we schedule automatic releases?

A release can only be created for Git-Tags. So the first step is to automatically create such a Tag. For this, we add a section to our .gitlab-ci.yml File, which is only executed, if it was called by the scheduler. (Which we will set up later)


In the example above, we use the API with an Access-Token authentification. To do so, such an Access-Token needs to be created. In the user settings under Access Tokens you can create such a token. The required permissions for this use case are api and write_repository.

After a click on “Create personal access token”, the token is shown only once. Just copy this value and store it as a variable in the Project settings CI / CD in the Section Variables. For our code example we need to call the variable GITLAB_ACCESS_TOKEN:

Important – especially for public repositories – is, to enable the Protected and Masked switches. Otherwise, other users can see and use your access token.
Because Project-Maintainers can also read protected variables, we created an extra, restricted user just for doing the release job.

To access the variable in the automatically created Tag, we need to protect this tag (otherwise protected variables can not be used). In our example above we defined that newly created Tags start with “Release”. So we will protect every Tag, which starts with that pattern:

After this step is completed, we can now configure the scheduler:

Here you can configure, how often the scheduler will be executed – so how often our release should be created. If none of the given settings is appropriate, we can define our own interval as a CRON-Format ( Since we defined in .gitlab-ci.yml that if it was called by the scheduler, a Tag should be created, this step is also completed.

To create a release from this Tag, we have to add another section to our .gitlab-ci.yml File, which is only executed, if it’s a Tag:


After that, every time the scheduler is executed, a Tag will be created from the Master-Branch. This Tag will then be released automatically.

To wrap everything up, here a complete demo .gitlab-ci.yml-File:


Join the conversation


  1. Hello Philipp,

    Thank you for your post. It really helped me.
    I want to add one comment. I’m using gitlab 12.9.2 and the 12.9 runner. For me the variable
    ${CI_PROJECT_PATH} didn’t have the ‘.git’ at the end of the path and it was failing because of it.
    Just in case somebody else get an error while running it.

  2. Hi,
    Thanks a lot for this article.
    I followed your steps because I want to run automatically a “build” script and then create a tag and a release with artifacts attached to that release. Everything works fine except the release step.
    At the ends I got one error:

    Missing environment variable ‘CI_COMMIT_TAG’: Releases can only be created on tag build.

    However, the tag was created at the previous stage. I use this toy repository to try to develop this
    Thank you a lot if you can help me

    1. Hello Germain,
      you tried to access CI_COMMIT_TAG in the step ‘tag_csv’ – in which the tag will be created. But CI_COMMIT_TAG is only available, if the script is executed in a tag (which would be publish_csv in your case).

      1. Hello,
        Ok, actually, the echo in “tag_csv” was just a test to check why I got that error message.

        Cleaning this and playing with except: – tag for the two first jobs, I now have a chain that works expect the release step. The error is about the access token. It says that the GITLAB_ACCESS_TOKEN is missing but I created one and the tag_csv step uses it to create the tag.
        Do I need to have another one specifically for the release ?

        Also I saw more recent project, based on python such as
        Should I switch to this ?

        1. Hey!
          It seems that you didn’t set the GITLAB_ACCESS_TOKEN as described in the article (successfully). Can you verify these settings? Since these are confidential settings (personal access tokens), I can’t verify them.
          I would guess there is a mistake in the step described in image number 2 (assigning the variable) or 3 (defining protected tags)

          Switching to another gitlab-release project should not help (in this case), since the current problem is the authentification
          Hope I could help you!

          1. Hey!
            It works ! Great !
            Thanks a lot for your so quick replies.
            Actually, the token was well defined but I had unprotected the tags to be able to remove them manually for testing, because I cannot create the tag if one already exist. That was the error, as you said in the article, the tag has to be protected to access private (the token) variables.

  3. Thank you a lot, I was struggling with it for a long time, but it finally works thanks to you!

  4. Thanks man. I was struggling with this for so long. Thanks a lot. Not able to find upvote option here. but hey pizza’s on me..

  5. Hi,

    Getting error while attaching the artifact to the release as below:

    $ gitlab-release –message ‘Automatic release’ ./Demo.txt
    Traceback (most recent call last):
    File “/usr/bin/gitlab-release”, line 150, in
    info = release.create_release(args.message, args.files)
    File “/usr/bin/gitlab-release”, line 90, in create_release
    links = [‘* ‘ + self.post_file(filename) for filename in files]
    File “/usr/bin/gitlab-release”, line 90, in
    links = [‘* ‘ + self.post_file(filename) for filename in files]
    File “/usr/bin/gitlab-release”, line 57, in post_file
    files = {‘file’: open(filename, ‘rb’)}
    FileNotFoundError: [Errno 2] No such file or directory: ‘./Demo.txt’

      1. Hi Philipp,

        Yes the build stage is execute and I can see and browse the artifact(Demo.txt) as well. Below is the code I am trying:

        – build
        – publish
        stage: build
        – merge_requests
        # Usually the application is compiled here
        – echo “Demo” > Demo.txt
        – “./Demo.txt”

        stage: build
        name: alpine/git
        entrypoint: [“”]
        – merge_requests
        – git config “${GITLAB_USER_EMAIL}”
        – git config “${GITLAB_USER_NAME}”
        – git remote add demo-tag-origin https://oauth2:${GITLAB_ACCESS_TOKEN}${CI_PROJECT_PATH}
        – git tag -a “Release_$(date +%Y-%m-%d)” -m “Auto-Release”
        – git push demo-tag-origin “Release_$(date +%Y-%m-%d)”

        image: inetprocess/gitlab-release
        stage: publish
        – tags
        – build:test
        – echo $(pwd)
        – gitlab-release –message ‘Automatic release’ ./Demo.txt

        1. Hey,
          you specified, that your build:test-Task will only be executed on merge-requests, but the publish-task is not a merge-request itself – therefore the Demo.txt-Asset is not accessibly during that stage

          1. Thanks.
            So I updated only clause in publish stage to run on merge_requests and tags both also but getting below error now:

            “Missing environment variable ‘CI_COMMIT_TAG’: Releases can only be created on tag build.”

            So basically, but I want to achieve here is, I want to build some go binaries during build stage (when only merge with master) and then publish those binaries as part of the release during publish stage. Is there any way i can do that?

  6. Hi Philipp,

    No i did not remove the remove the line “only: – tags” from publish stage. It is like below:

    – tags
    – merge_requests

    1. Ok, that’s unfortunately wrong. As said, releases can only be made out of tags. So the execution during merge_requests is not possible. Your CI-Job failed during the publish-execution, triggered from a merge request. It has to be triggered from a tag.

      1. OK. So what should be the workaround here considering I have to create binaries and attach them as release assests? Shall I build the binaries on. “only – tags” condition?

  7. Hi,
    I am struggling with ACCESS Token
    remote: HTTP Basic: Access denied
    fatal: Authentication failed for ‘

    Even I tried with oauth2 as username as well as below example

    For each project access token created, a bot user will also be created and added to the project with
    “Maintainer” level permissions.
    For the bot:

    The name is set to the name of the token.
    The username is set to project_{project_id}_bot

Leave a comment

Your email address will not be published. Required fields are marked *