How to create a custom Ocean integration in Port

Ocean is an open-source extensibility framework connecting third-party tools to Port. This is an overview on building a custom integration.

November 20, 2025

Ready to start?

How to create a custom Ocean integration in Port
Listen to article
00:00 00:00

Ocean is Port’s open-source extensibility framework that makes it easy to connect your existing infrastructure and third-party tools to your Port organization. Integrations are critical to making platform engineering work effectively at scale, and for unifying your software development lifecycle (SDLC) into a single platform. 

Ocean is designed to make your Port organization more powerful by giving you ways to build the specific tools you need. In this post, we’ll share an overview of how to create a custom Ocean integration, with examples from the Linear integration built by Mor Paz, Director of R&D at Port.

Day-0 steps before you build an integration

There are some important prerequisites to consider before you start building, which will save you time and effort later:

  • Existing integrations: Do you need to make a custom Ocean integration, or can you use one of Port’s out-of-the-box integrations?
  • Admin status: You’ll need to have admin permissions in your organization’s portal in order to build an integration. Make sure you have full access before you start.
  • Tool familiarity: Knowing the tool you’re trying to integrate beforehand is a must. Who provides the tool? How does your team use it? What data is exposed by the tool, and which roles should be able to access that data?
  • Day-1 planning: Defining your team’s needs before you start is critical for long-term success. What tools are immediately mandatory for your team? What security and compliance requirements have to be put in place from the outset? What infrastructure is needed, and how should your integration’s configuration mapping look?
  • API keys and endpoints: Gather the specific identifiers you need to connect your portal and the tool you’re trying to integrate, and plan your client structure accordingly.

This prep work might seem obvious, but it’s important; knowing your integration will work properly and actually serve your organization’s needs from the outset will make all the difference when your team starts using it. This is very similar to using a platform-as-a-product approach to IDP development. Without careful preparation, you may find that your integration doesn’t do enough for your team, or that necessary use cases have slipped through the cracks.

Building your integration

Ocean comes out of the box with a command-line interface (CLI) and a built-in command to scaffold a new blank integration that you can use as the base for your new integration. 

To start, you’ll need to have Python 3.11 or higher installed on your machine (tip: use pyenv to manage the Python installations on your machine). From there, follow Port’s Ocean documentation to install the Ocean CLI, create your integration project, and set up your developer environment.

$ python -m venv .venv

$ source .venv/bin/activate

$ pip install "port-ocean[cli]"

$ ocean new

=====================================================================================
          ::::::::       ::::::::       ::::::::::           :::        ::::    ::: 
        :+:    :+:     :+:    :+:      :+:                :+: :+:      :+:+:   :+:  
       +:+    +:+     +:+             +:+               +:+   +:+     :+:+:+  +:+   
      +#+    +:+     +#+             +#++:++#         +#++:++#++:    +#+ +:+ +#+    
     +#+    +#+     +#+             +#+              +#+     +#+    +#+  +#+#+#     
    #+#    #+#     #+#    #+#      #+#              #+#     #+#    #+#   #+#+#      
    ########       ########       ##########       ###     ###    ###    ####      
=====================================================================================
By: Port.io
🚢 Unloading cargo... Setting up your integration at the dock.
  [1/10] integration_name (Name of the integration): myIntegration
  [2/10] integration_slug (myintegration): my_integration
  [3/10] integration_short_description (A short description of the project): My custom integration made for Port
  [4/10] full_name (Your name): Monkey D. Luffy
  [5/10] email (Your address email <you@example.com>): straw@hat.com
  [6/10] release_date (2023-08-06): 2023-08-06
  [7/10] is_private_integration [y/n] (y): y
  [8/10] port_client_id (you can find it using: https://docs.port.io/build-your-software-catalog/custom-integration/api/#find-your-port-credentials): <your-port-client-id>
  [9/10] port_client_secret (you can find it using: https://docs.port.io/build-your-software-catalog/custom-integration/api/#find-your-port-credentials): <your-port-client-secret>
  [10/10] is_us_region [y/n] (n): y

🌊 Ahoy, Captain! Your project is ready to set sail into the vast ocean of possibilities!
Here are your next steps:

⚓️ Install necessary packages: Run cd ./my_integration && make install && . .venv/bin/activate to install all required packages for your project.
⚓️ Copy example env file: Run cp .env.example .env  and update your integration's configuration in the .env file.
⚓️ Set sail with Ocean: Run ocean sail to run the project using Ocean.

Port resources for your integration

Some config files will need to be added or modified to fit your integration’s needs:

  • .port/spec.yaml: Tells Port what inputs your integration requires, as well as what objects it is able to send to Port.
  • .port/resources/blueprints.json: Sets up the default blueprints created in Port’s data model when your integration is first installed.
  • .port/resources/port-app-config.yaml: Sets up the default integration mapping used to tell the integration how to parse responses from the third-party tool you’re integrating into Port entities. You can learn more about the structure of the port-app-config.yaml file with our mapping configuration docs.

These files will provide structure for your integration and help ensure the finished integration performs as intended. For the Linear integration, the content of the .port/resources/port-app-config.yaml file is as follows:

createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
 - kind: team
   selector:
     query: "true"
   port:
     entity:
       mappings:
         identifier: .key
         title: .name
         blueprint: '"linearTeam"'
         properties:
           description: .description
           workspaceName: .organization.name
           url: "\"https://linear.app/\" + .organization.urlKey + \"/team/\" + .key"
 - kind: label
   selector:
     query: "true"
   port:
     entity:
       mappings:
         identifier: .id
         title: .name
         blueprint: '"linearLabel"'
         properties:
           isGroup: .isGroup
         relations:
           parentLabel: .parent.id
           childLabels: "[.children.edges[].node.id]"
 - kind: issue
   selector:
     query: "true"
   port:
     entity:
       mappings:
         identifier: .identifier
         title: .title
         blueprint: '"linearIssue"'
         properties:
           url: .url
           status: .state.name
           assignee: .assignee.email
           creator: .creator.email
           priority: .priorityLabel
           created: .createdAt
           updated: .updatedAt
         relations:
           team: .team.key
           labels: .labelIds
           parentIssue: .parent.identifier

Providing parameters and writing integration code

Ocean comes out of the box with sane defaults that mean only a minimal set of parameters are required to get the integration up and running. After creating your scaffolding, your new integration will include a main.py file, which is the entrypoint used by the integration when it starts. This file includes comments and placeholders showing where and how your code should be added to the integration. The specific functions you need will dictate the coding required to complete your integration.

Refer to the Ocean documentation on integration configuration for specifics, but you shouldn’t need any custom configurations if your integration:

  1. Has a fixed set of resources
  2. Doesn’t need user-defined parameters
  3. Doesn’t need custom configuration for API calls
  4. Uses standard endpoints with fixed parameters

As you understand and define which environment variables you’ll use, you can easily set up these variables by creating a variables.sh file that includes the different variables you need. This way, when you run the file, it will always make sure the required variables are loaded. 

In the case of the Linear integration, Mor used the following in his file:

export OCEAN__PORT__CLIENT_ID='YOUR_PORT_CLIENT_ID'
export OCEAN__PORT__CLIENT_SECRET='YOUR_PORT_CLIENT_SECRET'
export OCEAN__EVENT_LISTENER__TYPE='POLLING'
export OCEAN__INTEGRATION__CONFIG__LINEAR_API_KEY='YOUR_LINEAR_API_KEY'

Ocean’s framework also has built-in support for multiple event listeners, including webhook events, so your integration can react and perform tasks based on events from both Port and the third-party service you’re integrating.

Running your Ocean integration

Once you’re ready to test your integration, you have two commands to choose from:

  1. ocean run : Uses the Ocean CLI to run the integration
  2. make run: Uses the command defined in the integration’s makefile to run the integration

Both commands achieve the same result, which is to run the integration and show you the various logs reported by the integration. In a newly-developed integration, this is the point where you’ll notice logical errors, like:

  • Creating blueprints fails because the blueprint schema in the blueprints.json file is wrong
  • You’re unable to query your third-party tool due to bad requests
  • Data doesn’t appear in your Port organization, or only some of the entity properties populate, due to issues in the port-app-config.yaml base mapping

You might also see an error about missing parameters required by the integration. If you do, make sure you’ve exported the environment variables required for your integration (as we covered in the Linear integration example with the variables.sh file).

Publishing your Ocean integration

Once you’ve got your integration running correctly, there are a few final things to take care of before it can be published. Follow these steps to finish the process of creating your integration:

  • Code cleanup: Verify there are no unused variables, imports, functions, unnecessary code comment blocks, prints, or debug logs. Ensure that all logs use the correct log level and all functions have a correct signature.
  • Linter check: Run the custom Ocean project linter using the make lint command. It will alert you if there are issues that need to be addressed before opening a pull request to the upstream Ocean repository.
  • Open a pull request: Once any issues in the linter check have been resolved, make sure your fork is synced with the latest version of Ocean’s repository, and your integration is using the latest version of Ocean’s library. Then, open your request and the Ocean team will review.

The Ocean team at Port may have suggestions or requests for fixes, if applicable. Once those have been addressed, they’ll approve and merge the integration, after which an automatic CI/CD process will be triggered that publishes your Ocean integration. Once published, you’ll be able to see your Ocean integration in the data sources menu in Port!

{{cta_7}}

Tags:

{{survey-buttons}}

Get your survey template today

By clicking this button, you agree to our Terms of Use and Privacy Policy
{{survey}}

Download your survey template today

By clicking this button, you agree to our Terms of Use and Privacy Policy
{{roadmap}}

Free Roadmap planner for Platform Engineering teams

  • Set Clear Goals for Your Portal

  • Define Features and Milestones

  • Stay Aligned and Keep Moving Forward

{{rfp}}

Free RFP template for Internal Developer Portal

Creating an RFP for an internal developer portal doesn’t have to be complex. Our template gives you a streamlined path to start strong and ensure you’re covering all the key details.

{{ai_jq}}

Leverage AI to generate optimized JQ commands

test them in real-time, and refine your approach instantly. This powerful tool lets you experiment, troubleshoot, and fine-tune your queries—taking your development workflow to the next level.

{{cta_1}}

Check out Port's pre-populated demo and see what it's all about.

Check live demo

No email required

{{cta_survey}}

Check out the 2025 State of Internal Developer Portals report

See the full report

No email required

{{cta_2}}

Minimize engineering chaos. Port serves as one central platform for all your needs.

Explore Port
{{cta_3}}

Act on every part of your SDLC in Port.

{{cta_4}}

Your team needs the right info at the right time. With Port's software catalog, they'll have it.

{{cta_5}}

Learn more about Port's agentic engineering platform

Read the launch blog

Let’s start
{{cta_6}}

Contact sales for a technical walkthrough of Port

Let’s start
{{cta_7}}

Every team is different. Port lets you design a developer experience that truly fits your org.

{{cta_8}}

As your org grows, so does complexity. Port scales your catalog, orchestration, and workflows seamlessly.

{{cta-demo}}
{{reading-box-backstage-vs-port}}
{{cta-backstage-docs-button}}

Example JSON block

{
  "foo": "bar"
}

Order Domain

{
  "properties": {},
  "relations": {},
  "title": "Orders",
  "identifier": "Orders"
}

Cart System

{
  "properties": {},
  "relations": {
    "domain": "Orders"
  },
  "identifier": "Cart",
  "title": "Cart"
}

Products System

{
  "properties": {},
  "relations": {
    "domain": "Orders"
  },
  "identifier": "Products",
  "title": "Products"
}

Cart Resource

{
  "properties": {
    "type": "postgress"
  },
  "relations": {},
  "icon": "GPU",
  "title": "Cart SQL database",
  "identifier": "cart-sql-sb"
}

Cart API

{
 "identifier": "CartAPI",
 "title": "Cart API",
 "blueprint": "API",
 "properties": {
   "type": "Open API"
 },
 "relations": {
   "provider": "CartService"
 },
 "icon": "Link"
}

Core Kafka Library

{
  "properties": {
    "type": "library"
  },
  "relations": {
    "system": "Cart"
  },
  "title": "Core Kafka Library",
  "identifier": "CoreKafkaLibrary"
}

Core Payment Library

{
  "properties": {
    "type": "library"
  },
  "relations": {
    "system": "Cart"
  },
  "title": "Core Payment Library",
  "identifier": "CorePaymentLibrary"
}

Cart Service JSON

{
 "identifier": "CartService",
 "title": "Cart Service",
 "blueprint": "Component",
 "properties": {
   "type": "service"
 },
 "relations": {
   "system": "Cart",
   "resources": [
     "cart-sql-sb"
   ],
   "consumesApi": [],
   "components": [
     "CorePaymentLibrary",
     "CoreKafkaLibrary"
   ]
 },
 "icon": "Cloud"
}

Products Service JSON

{
  "identifier": "ProductsService",
  "title": "Products Service",
  "blueprint": "Component",
  "properties": {
    "type": "service"
  },
  "relations": {
    "system": "Products",
    "consumesApi": [
      "CartAPI"
    ],
    "components": []
  }
}

Component Blueprint

{
 "identifier": "Component",
 "title": "Component",
 "icon": "Cloud",
 "schema": {
   "properties": {
     "type": {
       "enum": [
         "service",
         "library"
       ],
       "icon": "Docs",
       "type": "string",
       "enumColors": {
         "service": "blue",
         "library": "green"
       }
     }
   },
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "system": {
     "target": "System",
     "required": false,
     "many": false
   },
   "resources": {
     "target": "Resource",
     "required": false,
     "many": true
   },
   "consumesApi": {
     "target": "API",
     "required": false,
     "many": true
   },
   "components": {
     "target": "Component",
     "required": false,
     "many": true
   },
   "providesApi": {
     "target": "API",
     "required": false,
     "many": false
   }
 }
}

Resource Blueprint

{
 “identifier”: “Resource”,
 “title”: “Resource”,
 “icon”: “DevopsTool”,
 “schema”: {
   “properties”: {
     “type”: {
       “enum”: [
         “postgress”,
         “kafka-topic”,
         “rabbit-queue”,
         “s3-bucket”
       ],
       “icon”: “Docs”,
       “type”: “string”
     }
   },
   “required”: []
 },
 “mirrorProperties”: {},
 “formulaProperties”: {},
 “calculationProperties”: {},
 “relations”: {}
}

API Blueprint

{
 "identifier": "API",
 "title": "API",
 "icon": "Link",
 "schema": {
   "properties": {
     "type": {
       "type": "string",
       "enum": [
         "Open API",
         "grpc"
       ]
     }
   },
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "provider": {
     "target": "Component",
     "required": true,
     "many": false
   }
 }
}

Domain Blueprint

{
 "identifier": "Domain",
 "title": "Domain",
 "icon": "Server",
 "schema": {
   "properties": {},
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {}
}

System Blueprint

{
 "identifier": "System",
 "title": "System",
 "icon": "DevopsTool",
 "schema": {
   "properties": {},
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "domain": {
     "target": "Domain",
     "required": true,
     "many": false
   }
 }
}
{{tabel-1}}

Microservices SDLC

  • Scaffold a new microservice

  • Deploy (canary or blue-green)

  • Feature flagging

  • Revert

  • Lock deployments

  • Add Secret

  • Force merge pull request (skip tests on crises)

  • Add environment variable to service

  • Add IaC to the service

  • Upgrade package version

Development environments

  • Spin up a developer environment for 5 days

  • ETL mock data to environment

  • Invite developer to the environment

  • Extend TTL by 3 days

Cloud resources

  • Provision a cloud resource

  • Modify a cloud resource

  • Get permissions to access cloud resource

SRE actions

  • Update pod count

  • Update auto-scaling group

  • Execute incident response runbook automation

Data Engineering

  • Add / Remove / Update Column to table

  • Run Airflow DAG

  • Duplicate table

Backoffice

  • Change customer configuration

  • Update customer software version

  • Upgrade - Downgrade plan tier

  • Create - Delete customer

Machine learning actions

  • Train model

  • Pre-process dataset

  • Deploy

  • A/B testing traffic route

  • Revert

  • Spin up remote Jupyter notebook

{{tabel-2}}

Engineering tools

  • Observability

  • Tasks management

  • CI/CD

  • On-Call management

  • Troubleshooting tools

  • DevSecOps

  • Runbooks

Infrastructure

  • Cloud Resources

  • K8S

  • Containers & Serverless

  • IaC

  • Databases

  • Environments

  • Regions

Software and more

  • Microservices

  • Docker Images

  • Docs

  • APIs

  • 3rd parties

  • Runbooks

  • Cron jobs

Starting with Port is simple, fast and free.

Let’s start