Jump to content

Help:Toolforge/Migrate build service job to push to deploy

From Wikitech
Push-to-Deploy is currently a beta feature of Toolforge, existing features will be stable but expect some missing functionality and keep an eye for bugs.

This tutorial explains how to initialize your Toolforge configuration from an existing Toolforge job that uses an image built with the Build Service, and then configure Push-to-Deploy so that future Git pushes automatically trigger deployments.


If you already use the Build Service, most of the information needed to create the tool configuration can be generated automatically from your existing job.

The job we'll be migrating is a simple Toolforge job named recent-changes, that fetches the most recent changes on English Wikipedia.

The migration has two main stages:

  1. Create your tool configuration: Configure your tool to build and deploy your existing Build Service job and verify that the component works.
  2. Configure and enable Push-to-Deploy: Configure your Git repository to trigger deployments automatically when you push changes.
We are using a scheduled job as example, but continuous jobs and webservices are already supported, as long as they use Build Service built images.

Before you begin

This tutorial assumes that:

  • you have access to the Toolforge tool account
  • you already have a working Toolforge job
  • the job uses an image built with the Toolforge Build Service
  • you can commit and push changes to the job source repository

Create your tool configuration

Become your tool

Once you logged into login.toolforge.org, become your tool before running Toolforge commands in this tutorial:

myuser@tools-bastion-15:~$ become <TOOL NAME>

Replace <TOOL NAME> with the name of your Toolforge tool.

Inspect your existing job

First, inspect the current job configuration:

tools.komla-test3@tools-bastion-15:~$ toolforge jobs show recent-changes

For example, the recent-changes job looks similar to this:

Job name:    recent-changes
Command:     ./recent-changes-job.sh
Job type:    scheduled: @daily
Image:       tool-komla-test3/recent-changes:latest
File log:    no
Emails:      none
Resources:   default
Mounts:      all
Retry:       no
Timeout:     yes: 3600s
Status:      Pending for 50m59s

We see that we have one scheduled job named recent-changes that runs daily and is using the tool-komla-test3/recent-changes:latest image. When the image is in the format tool-<yourtool>/<imagename>:latest it means that it's a Build Service built image. Make sure that the job you choose uses one of these images.

Once you know which image it uses, you can also inspect previous builds:

tools.komla-test3@tools-bastion-15:~$ toolforge build list
build_id                                  status    start_time            end_time              source_url                                                image_name      ref    envvars    use_latest_versions    use_deprecated_versions    destination_image
komla-test3-buildpacks-pipelinerun-wbcvt  ok        2026-09-30T14:20:08Z  2026-09-30T14:20:43Z  https://gitlab.wikimedia.org/toolforge-repos/komla-test3  recent-changes  main   N/A        False                  False                      tools-harbor.wmcloud.org/tool-komla-test3/recent-changes:latest@sha256:dcd2241e25a8fa6c45c918623b1115fb189227608e961f3064865eed228fd55e
...

You can check the last build by running:

tools.komla-test3@tools-bastion-15:~$ toolforge build show
Build ID: komla-test3-buildpacks-pipelinerun-wbcvt
Start Time: 2026-09-30T14:20:08Z
End Time: 2026-09-30T14:20:43Z
Status: ok
Message: Tasks Completed: 1 (Failed: 0, Cancelled 0), Skipped: 0
Parameters:
    Source URL: https://gitlab.wikimedia.org/toolforge-repos/komla-test3
    Image Name: recent-changes
    Ref: main
    Envvars: N/A
Use latest versions: False
Use deprecated versions: False
Destination Image: tools-harbor.wmcloud.org/tool-komla-test3/recent-changes:latest@sha256:dcd2241e25a8fa6c45c918623b1115fb189227608e961f3064865eed228fd55e

Note that the Destination Image matches the image in our job.

Generate a Tool configuration

You can start from scratch, or try generating an example configuration from your existing jobs.

Run:

tools.komla-test3@tools-bastion-15:~$ toolforge components config generate
Warning: Note that this config is an autogenerated example, please double check and validate before using it
config_version: v1beta1
components:
  recent-changes:
    build:
      repository: https://gitlab.wikimedia.org/toolforge-repos/komla-test3
      ref: main
    run:
      command: python recent-changes.py
      schedule: '@daily'
      timeout: 3600

Note the warning on the first line, make sure to verify the generated configuration. It will also show all the jobs it was able to detect (continuous and scheduled jobs that use build-service images), you can add them one by one, no need to add them all at the same time.

For details on what to put in the configuration, see Help:Toolforge/Deploy your tool#Creating your tool configuration, but here's a high level overview of the config we got:

# Version of the configuration schema, not mandatory but helps to know what schema to use
config_version: v1beta1
# Here we list each of our components, each component is made of a `build` (like an image) and a `run` (like a job)
components:
  # Our first component, called `recent-changes`
  recent-changes:
    build:
      # Public url to the git repository
      repository: https://gitlab.wikimedia.org/toolforge-repos/komla-test3
      # Branch
      ref: main

    # This is the job to run
    run:
      # same as `--command` in `toolforge jobs`
      command: python recent-changes.py
      # this would be the `--schedule`
      schedule: '@daily'
      # and this would be the `--timeout`
      timeout: 3600

Note that you can also run a continuous job (ex. if you don't pass schedule) or even a webservice/published continuous job, like the example in the docs.

The fields match the options that are passed on the toolforge jobs cli, with minor changes like --mem becomes memory:, you can see the full list in the API docs and a jsonschema version in the repo (that you can use for your editor autocomplete).

Save and review the generated configuration

You can save this configuration in a file, and edit live (we will see when configuring Push-to-Deploy how to tell it to fetch it from your git repository instead):

tools.komla-test3@tools-bastion-15:~$ toolforge components config generate > toolforge.yaml

tools.komla-test3@tools-bastion-15:~$ vim toolforge.yaml  # or use your preferred editor

Make sure to check that the parameters set there match the ones for your job, and add/remove/modify them if they do not. Delete any components that you don't wont to configure now if there's more than one.

Create the configuration in Toolforge

Once reviewed, you can create the configuration for your tool with:

tools.komla-test3@tools-bastion-15:~$ toolforge components config create toolforge.yaml

# You can double check your current config with
tools.komla-test3@tools-bastion-15:~$ toolforge components config show
tools.komla-test3@tools-bastion-15:~$ toolforge components config show
Warning: You are using a beta feature of Toolforge.
config_version: v1beta1
components:
  recent-changes:
    build:
      repository: https://gitlab.wikimedia.org/toolforge-repos/komla-test3
      ref: main
    run:
      command: python recent-changes.py
      schedule: '@daily'
      timeout: 3600


Create a deployment to test it worked

Now that you have a configuration, you can create deployments. A deployment will create all the images needed by all your jobs, and update/create your jobs if needed.

tools.komla-test3@tools-bastion-15:~$ toolforge components deployment create
Deployment ID: 20261007-170802-v8e5r8zyz8
Created: 20261007-170802

Builds:
  recent-changes(pending): no-id-yet

Runs:
  recent-changes(pending):

For other options during deployment see Help:Toolforge/Deploy your tool#Creating a deployment.

Now you can check the status of the deployment, it will show you the builds that it triggered, and the runs it created (when it does). You can keep checking until it passes:

tools.komla-test3@tools-bastion-15:~$ toolforge components deployment show
Deployment ID: 20261007-170942-psayjz3xf9
Created: 20261007-170942
Status: successful
Long status:
  Finished at 2026-10-07 17:10:20.063507+00:00

Builds:
  recent-changes(successful): id:komla-test3-buildpacks-pipelinerun-jthrj You can see the logs with `toolforge build logs komla-test3-buildpacks-pipelinerun-jthrj`

Runs:
  recent-changes(successful): created or updated job recent-changes, [info](Job recent-changes was updated in storage and runtime)

Tool config:
  components:
    recent-changes:
      build:
        ref: main
        repository: https://gitlab.wikimedia.org/toolforge-repos/komla-test3
      run:
        command: python recent-changes.py
        schedule: '@daily'
        timeout: 3600
  config_version: v1beta1

And done! Your job should now be running with the newly built image, you will notice that now the image has a sha256:... trailing the image name, that means that it worked:

tools.komla-test3@tools-bastion-15:~$ toolforge jobs show recent-changes
...
+---------------+----------------------------------------------------------------------------------------------------------------+
| Image:        | tool-komla-test3/recent-changes:latest@sha256:b88c7d086041dfdc1ea91505ae65177aa6bae42adc0077049a97b1e32ac6c8d0 |
+---------------+----------------------------------------------------------------------------------------------------------------+
...


Configure and enable Push-to-Deploy

Push-to-Deploy allows a CI runner to trigger a deployment after changes are pushed to your Git repository.

Below are the steps for a basic configuration. Visit Triggering a deployment from your CI runner for more details.

Add the configuration to your source repository

This step is not strictly needed, but it enables not having to ssh to toolforge to change your configuration, and to keep the changes to it tracked in a Git repository.

The tool configuration can be hosted anywhere that is publicly accessible thorugh http/https.

For this tutorial, save the generated configuration as toolforge.yaml in the same Git repository as the source code. Keeping the source code and deployment configuration together makes it easier to track changes to both in Git.

You can use any publicly accessible Git repository. For Toolforge projects, we recommend using a repository in the Wikimedia GitLab toolforge-repos namespace (you can create them from the toolsadmin page of your tool).

For example:

my-project/
├── Procfile
├── requirements.txt
├── source-files...
└── toolforge.yaml


There's one change you need to make to your configuration before commiting it, we have to add source_url:

config_version: v1beta1

# We only added the nex entry
# This url should point to the "raw" version of your file in your hosting, this example uses gitlab
source_url: https://gitlab.wikimedia.org/toolforge-repos/komla-test3/-/raw/main/toolforge.yaml?ref_type=head

components:
  recent-changes:
    build:
      repository: https://gitlab.wikimedia.org/toolforge-repos/komla-test3
      ref: main

    run:
      command: python recent-changes.py
      schedule: '@daily'
      timeout: 3600

Commit and push the file:

mylaptop$ git add toolforge.yaml
mylaptop$ git commit -m "Add Toolforge configuration"
mylaptop$ git push

Update Tool configuration

Once toolforge.yaml is publicly accessible, you can update your tool configuration with a single command, using the url you added as source_url:

tools.komla-test3@tools-bastion-15:~$ curl 'https://gitlab.wikimedia.org/toolforge-repos/komla-test3/-/raw/main/toolforge.yaml?ref_type=heads' | toolforge components config create

You will see information saying configuration created or updated successfully.

This avoids having to clone the repository or download toolforge.yaml on the Toolforge bastion at all.

From now on Toolforge will update your configuration at the start of every deployment, pulling it from that URL, so from now on you don't need to change it manually from the bastion anymore.

Configure your CI to trigger a deployment

The following is a minimal example for Wikimedia GitLab. For additional options and details, see Triggering a deployment from your CI runner.

Create a deploy token

While logged in as your tool account, create a deploy token:

tools.komla-test3@tools-bastion-15:~$ toolforge components deploy-token create
Warning: You are using a beta feature of Toolforge.
Token: SOME-LONG-UUID-KIND-OF-STRING
Created: 2026-09-30T14:10:28.538502Z

The command displays a token. Treat this token as a secret. Do not commit it to your repository.

Add the deploy token to GitLab

In your GitLab repository, go to:

Settings → CI/CD → Variables

Create a variable named:

TOOLS_DEPLOY_TOKEN

Set its value to the deploy token created in the previous step.

For security, make the variable hidden, protected, and masked where appropriate.

Add a GitLab CI configuration

Toolforge deploy pipeline on gitlab
Toolforge deploy pipeline on gitlab

Create a .gitlab-ci.yml file in the repository containing:

.gitlab-ci.yml
include:
  - project: "repos/cloud/cicd/gitlab-ci"
    file: "toolforge-cd/deploy-to-toolforge.yaml"

The included Toolforge CI configuration handles triggering the deployment.

This minimal configuration assumes that the GitLab project name and Toolforge tool name are the same. If they are different, or if you need to customize the deployment process, see Triggering a deployment from your CI runner.

Commit and push .gitlab-ci.yml to the repository.

When a commit is pushed or merged to the default branch, GitLab CI will use the deploy token to request a new deployment for komla-test3.


You should see now when you pushed that change, that a new Gitlab pipeline triggered and did a deploy (see the screenshot), congratulations!

The setup gives the CI runner the credentials it needs to request a deployment for your tool.

Avoid independently managing the migrated job

After migrating your job, any manual changes that you make will be overwritten by Toolforge on the next deployment. So if you want them to last remember to change your Toolforge configuration in your git repository instead.

What changed?

Before migration, the deployment workflow is approximately:

Git push
      ↓
Ssh login.toolforge.org
      ↓
become <mytool>
      ↓
toolforge build start ...
      ↓
toolforge jobs delete/run/restart ...

After migrating to using a Tool configuration and cnfiguring Push-to-Deploy, the workflow becomes:

Git push
      ↓
CI triggers deployment
      ↓
Toolforge builds your images
      ↓
Toolforge updates your jobs


This allows you to change your code, and get it deployed without any more action on your side than git push.

Troubleshooting

Check the generated configuration

If the generated configuration does not look correct, do not deploy it immediately.

Compare it with:

myuser@tools-bastion-15:~$ toolforge jobs list -o long
myuser@tools-bastion-15:~$ toolforge build list

Correct toolforge.yaml before creating the Tool configuration.

Check the Tool configuration

Run:

myuser@tools-bastion-15:~$ toolforge components config show

Verify that the repository, ref, component name, command, schedule, and other settings are correct.

Check deployment history

Run:

myuser@tools-bastion-15:~$ toolforge components deployment list

Use the deployment information to determine whether the build or deployment failed.

Check your CI pipeline

If pushing a change does not create a deployment, check your repository's CI pipeline.

Verify that:

  • the CI job ran after the Git push;
  • the CI job completed successfully;
  • the credentials required to trigger a Toolforge deployment are configured correctly.

See Triggering a deployment from your CI runner for the current setup instructions.

Rebuild the image manually

By default, a new deployment will not rebuild an image if the source code for it did not change. Sometimes for troubleshooting, you want force build the image even when the code did not change, for that you can run:

$ toolforge components deployment create --force-build

This creates a deployment that bypasses the git hash check, and rebuilds the image even if the code did not change.

Force recreating the job on deployment

Also by default, a deployment will not recreate a job if the image it uses did not change (did not get built) and the job already exists with the same configuration, but sometimes you want to force it to delete the job and createn anew, for that you can pass:

$ toolforge components deployment create --force-run

Failing to generate config from a running job

Make sure the job is using a build service image and not just a standard Toolforge image. Note that not all the jobs are supported (ex. one-off jobs or webservices started with toolforge webservice).

Communication and support

Support and administration of the WMCS resources is provided by Wikimedia Foundation staff and Wikimedia movement volunteers. Please reach out with questions and join the conversation:

Discuss and receive general support
  • Chat in real time in the IRC channel #wikimedia-cloud connect or the bridged Telegram group
  • Discuss via email after you have subscribed to the cloud@ mailing list
Stay aware of critical changes and plans
Track work tasks and report bugs

Use a subproject of the #Cloud-Services Phabricator project to track confirmed bug reports and feature requests about the Cloud Services infrastructure itself

Read stories and WMCS blog posts

Read posts about Cloud Services on the Wikimedia Technical Blog. (Older posts can be read from the Cloud Services Blog.)

See also