How to use multiple workshops in a project

Workshops← Multi-workshop patterns|Status diagrams →

A project may require different toolchains for different components, such as a Go backend and a Node.js frontend. Instead of putting everything into a single workshop definition, you can define multiple workshops in the same project directory. Each workshop is an independent environment with its own base image, SDKs, and actions; at the same time, the workshops share a single project directory mounted at /project/.

Set up definitions

When a project uses multiple workshops, store their definitions in the .workshop/ subdirectory instead of a single workshop.yaml in the project root. Each file must be named after its workshop, so the name field matches the file name (without the .yaml extension).

Here is a project layout with two workshop definitions and a shared in-project SDK:

my-project/
├── .workshop/
│   ├── frontend.yaml
│   ├── backend.yaml
│   └── common-tools/
│       └── sdk.yaml
├── web/
└── api/

The frontend workshop uses the node SDK for the browser-facing part of the project:

.workshop/frontend.yaml
name: frontend
base: ubuntu@24.04
sdks:
  - name: node
    channel: 24
actions:
  build: |
    npm run build

The backend workshop uses the go SDK for the server-side code:

.workshop/backend.yaml
name: backend
base: ubuntu@22.04
sdks:
  - name: go
    channel: 1.26
actions:
  test: |
    go test ./...

Each workshop can use a different base image, a different set of SDKs, and its own actions, all while sharing the project directory.

What’s more, you can share in-project SDKs across workshops, as described in a section below.

Note

You cannot mix a root-level workshop.yaml with files in .workshop/. If Workshop finds both, it reports an error.

Launch and manage workshops

Launch both workshops at once:

$ workshop launch frontend backend

When a project has multiple workshops, the workshop name is required in every command; you cannot omit it as you would with a single-workshop project.

Check the status of both workshops:

$ workshop list

  WORKSHOP  STATUS  NOTES
  frontend  Ready   -
  backend   Ready   -

Run an action in a specific workshop:

$ workshop run frontend -- build
$ workshop run backend -- test

Shell into one of the workshops:

$ workshop shell backend

Execute a one-off command:

$ workshop exec frontend -- node --version

Stop and start workshops independently:

$ workshop stop frontend
$ workshop start frontend

Or stop both at once:

$ workshop stop frontend backend

To see the status of workshops in the current project, use the workshop list command without arguments:

$ workshop list

  WORKSHOP  STATUS  NOTES
  frontend  Ready   -
  backend   Ready   -

To see the status of workshops across all projects on the system, use the --global flag:

$ workshop list --global

  PROJECT                   WORKSHOP  STATUS  NOTES
  /home/user/my-project     frontend  Ready   -
  /home/user/my-project     backend   Ready   -
  /home/user/other-project  dev       Ready   -

When you no longer need the workshops, remove them:

$ workshop remove frontend backend

Share in-project tools

If multiple workshops need the same custom tooling, define an in-project SDK rather than duplicating hooks or configuration. In-project SDKs are stored in subdirectories of .workshop/ and referenced with the project- prefix.

Both workshops can then include it:

.workshop/frontend.yaml
name: frontend
base: ubuntu@24.04
sdks:
  - name: node
    channel: 24
  - name: project-common-tools
.workshop/backend.yaml
name: backend
base: ubuntu@22.04
sdks:
  - name: go
    channel: 1.26
  - name: project-common-tools

After adding the SDK references, refresh the workshops to pick up the change:

$ workshop refresh frontend backend

Cross-workshop networking

Workshops in the same project can reach each other directly by hostname. For instance, given a running backend workshop, the frontend counterpart can reach it by the short form backend. If backend exposes an API on port 8080, reach it directly from frontend:

$ workshop exec frontend -- curl http://backend:8080

Note

Direct access works only if the service listens on all interfaces (for example, 0.0.0.0:8080) rather than only on loopback. Any device that can reach the workshopbr0 bridge can resolve the workshop’s *.wp hostname through it and connect too, so restrict such a service with a firewall if it isn’t meant to be shared.

From outside the project, use the full hostname shown by workshop info backend (usually backend.<PROJECT>.wp, otherwise backend.<PROJECT-ID>.wp). For SSH-based tools, Workshop also generates an OpenSSH configuration on the host that trusts workshop host keys signed by its per-user authority and authorizes your user to connect, so tools such as ssh and VS Code Remote-SSH reach a workshop by its hostname without a host-key or password prompt. See How to connect your local VS Code to a workshop for a worked example.

Use hostname-based access for anything TCP or HTTP between workshops in the same project; it needs no setup. Use the tunnel interface instead to expose a service to the host itself, to reach a workshop in a different project, or when you need workshop connect-level control over who can reach the port.

You cannot connect a plug in one workshop to a slot in another; workshop connect rejects such attempts. However, all workshops on the same machine share a common host, and the tunnel interface can bridge through it.

The idea is to compose two independent tunnels: one that exposes a service from the backend workshop to the host, and another that lets the frontend workshop reach that host port. This is different from a regular intraworkshop connection, where a single tunnel links a plug to a slot inside the same workshop. Here, the host sits in the middle, and each workshop configures its own half of the bridge.

The backend workshop exposes its API on the host by pairing a system plug with a regular SDK slot:

.workshop/backend.yaml
name: backend
base: ubuntu@22.04
sdks:
  - name: go
    channel: 1.26
    slots:
      api:
        interface: tunnel
        endpoint: localhost:8080    # service inside the workshop
  - name: system
    plugs:
      api:
        interface: tunnel
        endpoint: localhost:8080    # port on the host

The frontend workshop reaches the host port by pairing a regular SDK plug with a system slot:

.workshop/frontend.yaml
name: frontend
base: ubuntu@24.04
sdks:
  - name: node
    channel: 24
    plugs:
      api:
        interface: tunnel
        endpoint: localhost:8080    # where the client connects
  - name: system
    slots:
      api:
        interface: tunnel
        endpoint: localhost:8080    # host-side port (bridged from backend)

Launch both workshops. The backend tunnel auto-connects because its plug is on the system SDK with a matching name, but the frontend tunnel requires a manual step:

$ workshop launch frontend backend
$ workshop connect frontend/node:api

Verify the connection:

$ workshop connections frontend

  INTERFACE  PLUG                   SLOT                   NOTES
  tunnel     frontend/node:api      frontend/system:api    manual

After this, any service listening on port 8080 inside the backend workshop is reachable at localhost:8080 from within the frontend workshop.

Note

The host port must be free before launching the backend workshop, or the tunnel will fail to activate. If you need several cross-workshop tunnels, use a different port for each. See How to forward ports with tunneling for tunnel basics and troubleshooting.

See also

Explanation:

How-to guides:

Reference: