Skip to content

Getting started - creating services

Turn your analysis code into production-ready services - no infrastructure or DevOps skills needed - and share with your team and beyond.

This guide walks you through creating an example Twined service deployed in the cloud. By the end, you'll have a real service and be able to ask it questions from your computer and receive answers from it over the internet.

Tip

You can see our deployed example service here on GitHub.

Prerequisites

Before you begin, ensure you:

Create and clone a GitHub repository

Warning

The repository must be created in the same GitHub account used for your Twined service network. See this issue for more details.

Create a git repository for the service in the GitHub account linked to the Twined service network. Clone this repository to your computer and checkout a new branch called add-new-service. Replace <handle> with the handle of the GitHub account.

git clone https://github.com/<handle>/example-service.git
cd example-service
git checkout -b add-new-service

Install the python dependencies

Create a pyproject.toml file to define the service as a python package and list its dependencies:

[tool.poetry]
name = "example-service"
version = "0.1.0"
description = "An example Twined data service."
authors = ["Your name <your email>"]
packages = [{include = "example_service"}]

[tool.poetry.dependencies]
python = "^3.11"
octue = "0.69.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

Tip

We use Poetry in this example but you can use Pip or another package manager with either a setup.py or pyproject.toml file.

Now install the dependencies:

poetry install

Note that this will create a poetry.lock file with the locked dependency versions in. Keep this file.

Write the service python code

We'll make the example service calculate the first n numbers in the Fibonacci sequence.

Create a directory called example_service with an empty __init__.py file and an app.py file inside. In app.py, add the following code:

import json
import logging
import time

from octue.resources import Datafile, Dataset
from octue.twined.resources.example import calculate_fibonacci_sequence

logger = logging.getLogger(__name__)


def run(analysis):
    logger.info("Started example analysis.")

    # Get your input values...
    n = analysis.input_values["n"]

    # Do your calculations here...
    sequence = calculate_fibonacci_sequence(n)
    time.sleep(2)

    # Return results by assigning output values...
    analysis.output_values = {"fibonacci": sequence}

    # If output values are too large, or custom/binary file outputs
    # are required, you can save them as Datafiles and add them to
    # the output manifest...
    with Datafile("fibonacci.json", mode="w") as (datafile, f):
        json.dump(analysis.output_values, f)

    analysis.output_manifest.datasets["example_dataset"] = Dataset(files={datafile})
    logger.info("Finished example analysis.")

Add the Twine file

The Twine file is a JSON file containing JSON schemas stating what kind of data is expected as inputs and outputs of the service. It shows users what can be sent to the service and what to expect to receive. Inputs and outputs that violate the schemas won't be processed and will cause an error.

Create a file at the top level of the repository called twine.json:

{
  "input_values_schema": {
    "type": "object",
    "required": ["n"],
    "properties": {
      "n": {
        "type": "integer",
        "minimum": 0
      }
    }
  },
  "output_values_schema": {
    "type": "object",
    "required": ["fibonacci"],
    "properties": {
      "fibonacci": {
        "type": "array",
        "items": {
          "type": "integer"
        }
      }
    }
  },
  "output_manifest": {
    "datasets": {
      "example_dataset": {}
    }
  }
}

Tip

Learn more about twine.json files here.

Add the octue.yaml file

The service configuration file (called octue.yaml) names the service and sets details like where to store output data. Create an octue.yaml file at the top level of the repository, replacing:

  • <handle> with the GitHub account handle
  • <gcp-project-id> with the ID of the Google Cloud Platform (GCP) project the Twined service network is deployed in
  • <region> with the name of the GCP region the service network is deployed in (e.g. europe-west1)
  • <environment> with the name of the environment the service network is deployed in (main by default)

Tip

Ask the manager of your Twined service network to help you with these values!

services:
  - namespace: <handle>
    name: example-service
    app_source_path: example_service
    event_store_table_id: octue_twined.service-events
    diagnostics_cloud_path: gs://<gcp-project-id>-octue-twined/example-service/diagnostics
    output_location: gs://<gcp-project-id>-octue-twined/example-service/outputs
    service_registries:
      - name: <handle>'s services
        endpoint: https://<region>-<gcp-project-id>.cloudfunctions.net/<environment>-octue-twined-service-registry

Tip

Learn more about octue.yaml files here.

Enable GitHub Actions in the repository

Go back to your repository on GitHub and open its Actions settings (Settings -> Actions -> General). Set the "Actions permissions" option to "Allow all actions and reusable workflows".

Add the GitHub Actions deploy workflow

A GitHub Actions reusable workflow is used to automatically deploy the service when its code is merged into main. Create a file called .github/workflows/deploy.yml and add the following, replacing <handle> and <gcp-project-id> as before:

name: deploy

# Only trigger when a pull request into main branch is merged.
on:
  push:
    branches:
      - main

jobs:
  deploy:
    uses: octue/workflows/.github/workflows/build-twined-service.yml@0.11.0
    permissions:
      id-token: write
      contents: read
    with:
      gcp_project_name: <gcp-project-id>
      gcp_project_number: <gcp-project-number>
      gcp_region: <gcp-project-region>
      service_namespace: <handle>
      service_name: example-service

Tip

See here for more information, including how to use custom dockerfiles for your service.

Check your files

Once you've finished the steps above, your repository should have a file structure like this:

.
├── .github
│   └── workflows
│       └── deploy.yml
├── example_service
│   ├── __init__.py
│   └── app.py
├── octue.yaml
├── poetry.lock
├── pyproject.toml
└── twine.json

Merge the code into main

To deploy the service, we need to merge the code we've added into the main branch. Make sure any sensitive and irrelevant files are listed in a .gitignore file and run:

git add .
git commit -m "Add example Twined service"
git push

For best practice, open a pull request for your branch into main, review it, and merge it. For a simpler route:

git checkout main
git merge add-new-service
git push

Navigate to your repository's "Actions" tab on GitHub and you should see the deploy workflow progressing after a few seconds. An in-progress indicator (currently a small orange circle) will be shown against the most recent commit. Once the workflow has completed (which should take only a couple of minutes for this simple example service), a green tick should show next to the most recent commit.


GitHub repository "Actions" tab
The "Actions" tab of the GitHub repository. A successful workflow run is shown at the top with a green tick while a failed one is shown below it with a red cross. Note that the workflow in the image is called "release.yaml" but yours is called "deploy.yaml".

Ask the service its first question

You can now communicate with the service over the internet to ask it a question! Follow the authentication instructions in the terminal where you ran poetry install, replace <handle> as before, and run:

octue twined question ask <handle>/example-service:0.1.0 --input-values='{"n": 10}'

After a couple of minutes (while the Kubernetes cluster is spinning up a container to run the service), you should see log messages start to appear. Finally, the first 10 values in the Fibonacci sequence are returned as output values:

[2025-11-05 12:45:29,861 | INFO | octue.twined.resources.example] Starting Fibonacci sequence calculation.
[2025-11-05 12:45:29,861 | INFO | octue.twined.resources.example] Finished Fibonacci sequence calculation.
{"kind": "result", "output_values": {"fibonacci": [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]}, "output_manifest": null}

Tip

See the using services getting started guide to see how to ask questions in python instead.

Next steps

Success

Congratulations on creating your first service! For additional information, check out the following resources:

Troubleshooting

Failure

If you get this error, double check you've followed the authentication instructions fully.

octue.exceptions.CloudLocationNotSpecified: `project_id` must be specified for a service to connect to the correct service - received None.

Failure

If your question gets stuck with a log message saying Waiting for question to be accepted..., take a look at the troubleshooting infrastructure guide.