To get the most out of ephemeral developer environments, it is important to let Gitpod know how to build your project. We can achieve this by defining tasks in the .gitpod.yml configuration file.

Note: In your local developer environment, you likely set up your project only once. If you work in a team, you probably have written instructions on how to get started. With Gitpod, you automate these manual steps so that a new environment can be set up repeatedly by Gitpod.

Tasks are shell scripts that run on top of the Docker image you configure (learn more about custom Docker images).

Task types and execution order

Tasks in Gitpod are categorized into three types:

  • before: Preparatory steps that should run before the main setup, such as setting up tools or permissions.
  • init: Tasks for setting up the project, like downloading dependencies and compiling code.
  • command: Commands that start your main application or services.

The execution of these tasks may vary depending on whether you use Prebuilds. Prebuilds have been updated to trigger less frequently, now by default occurring on every 20th commit to reduce resource consumption and improve efficiency.

Important Update: With these recent changes, prebuilds now default to running less frequently. This means that a workspace may start from an outdated prebuild, leading to all three tasks (before, init, and command) running at the start. This ensures that any changes since the last prebuild are applied. However, when starting with an outdated prebuild, the workspace usually has all necessary libraries, docker images, etc., already cached from the previous prebuild run, so the init task should, in most cases, simply verify that no updates are needed (effectively a no-op).


  • Any file changes made outside of /workspace file hierarchy from init tasks will be lost on workspace start when prebuilds are enabled. Learn more
  • User specific environment variables are not loaded automatically for init and before tasks but can be loaded if you want. Learn more


Prebuilds allow for pre caching of dependencies and other setup steps. Prebuilds run in the background and execute the before and init tasks. Workspaces are then created based on the result of the most recent prebuild, which allows for faster workspace startup times.

The init task is where you want to do things like:

  • Download & install dependencies
  • Compile your source code
  • Run database migrations
  • Run code generation
  • Any other long-running, terminating processes necessary to prepare your project

Enable prebuilds, and let Gitpod run the time-consuming init tasks continuously behind the scene so you and anyone who opens your project on Gitpod doesn’t have to wait.

Restart a Workspace

When you restart a workspace, Gitpod already executed the init task (see above) either as part of a Prebuild or when you started the workspace for the first time.

As part of a workspace restart, Gitpod executes the before and command tasks:

Restart a workspace Restart a workspace

Start a Snapshot

When you start a snapshot, Gitpod already executed the init task (see above) either as part of a Prebuild or when you or a team member started the snapshot’s initial workspace for the first time.

As part of starting a snapshot, Gitpod executes the before and command tasks:

Start a snapshot Start a snapshot

Configure the terminal

You can configure where terminals open using the openMode properties below. Please note that this information is used if no previous terminals in the layout exist. Snapshots will first try to reuse existing terminals in the layout, before opening new ones.

language icon yml
    - name: Static Server
      command: python3 -m http.server 8080
    - name: DB Server
      command: sh ./scripts/start-db.sh
      openMode: split-right


You can configure how the terminal should be opened relative to the previous task.

openMode Description
openMode: tab-after Opens in the same tab group right after the previous tab
openMode: tab-before Opens in the same tab group left before the previous tab
openMode: split-right Splits and adds the terminal to the right
openMode: split-left Splits and adds the terminal to the left
openMode: split-top Deprecated. Splits and adds the terminal to the top
openMode: split-bottom Deprecated. Splits and adds the terminal to the bottom

Example Tasks

The examples below are common use cases you can get inspired by and adjust for your project’s needs.

Note: before and init tasks need to terminate while command can run indefinitely (i.e. until cancelled with Ctrl + C). This is because before and init may run as part of a prebuild and if these tasks do not terminate, the prebuild will eventually fail with a timeout.

One-line tasks

Each task contains a single npm command. The init task terminates once the dependencies are installed while the command task starts a development server and does not terminate.

language icon yml
    - name: Dev Server
      init: npm install
      command: npm run dev

Multi-line tasks

To run multiple commands for a given task, you can use the | notation where each line below (make sure you indent correctly) runs in sequence once the previous command terminates.

In the following example, the init task installs dependencies and configures a database. Then, the command task starts the dev server(s).

Note: In case of multiple terminals, there is no guarantee on the order in which tasks execute. The only guarantee you have is that before, init and command execute in that sequence per terminal.

language icon yml
    - name: Dependencies & Database
      init: |
          npm install
          npm run configure-database
      command: npm run dev

Note: This doesn’t stop execution on errors. If npm install in the example above fails, the npm run configure-database will still run. See how to exit after failure below for a workaround.

Wait for commands to complete

When working with multiple terminals, you may have a situation where terminal 1 runs build scripts and terminal 2 and 3 require that these scripts complete first. This can be achieved with gp sync-await and gp sync-done.

language icon yml
    - name: Rails
      init: >
          bundle install &&
          yarn install --check-files &&
          rails db:setup &&
          gp sync-done bundle # 'bundle' is an arbitrary name
      command: rails server

    - name: Webpack
      init: gp sync-await bundle # wait for the above 'init' to finish
      command: bin/webpack-dev-server

    - name: Redis
      init: gp sync-await bundle
      command: redis-server

    - name: Sidekiq
      init: gp sync-await bundle
      command: sidekiq

Wait for a port to be available

Let’s say you have a web app dev server that takes a moment to start up to listen on port 3000. Once it’s up and running, you want to run end-to-end tests against http://localhost:3000.

You can achieve this with two terminals and the gp ports await CLI command.

language icon yml
    - name: Dev Server
      init: npm install
      command: npm run dev

    - name: e2e Tests
      command: |
          gp ports await 3000
          npm run test

Immediately exit for any command failure within a task

If you wish to halt an entire task with for an error within the task script, then you could do the following:

language icon yml
    - init: |
            set -e # Tells bash to immediately exit on failure off a command
            bundle install
            yarn install --frozen-lockfile
            bundle exec rake
            bundle exec nanoc compile

Gitpod starts all your tasks inside separate bash ($SHELL) shells. Gitpod can only assert the exit status of the shell process of a task. Normally bash or other shells don’t halt on a failure of a command unless you explicitly ask it to. bash only inherits the last exit status of a script run with it before it’s own exit. Hence Gitpod can’t determine if all of your commands inside the init task succeeded. To have that effect, you can put set -e; on top of task shell-commands and wrap your whole task-script with () to configure that particular task shell to halt and immediately exit with an error code for a failure of any command. This can be specially helpful for prebuilds (i.e init tasks)

Missing examples?

We’d love to hear from you if you have specific questions or ideas for additional examples. Please click the following link to open a pre-configured GitHub issue: Ask for a new Start Task example.

Was this helpful?