Lock and unlock services
Overviewโ
This guide demonstrates a comprehensive service locking mechanism in Port that allows you to lock and unlock services during critical periods while ensuring deployment safety through automated checks.
We will leverage on two approaches for implementing the service locking and unlocking functionality, depending on your needs:
-
Basic locking without deployment checks: Uses self-service actions with synced webhooks to update service lock status directly.
-
Advanced locking with deployment checks: Integrates with your CI/CD pipeline (GitHub workflows) to automatically check service lock status before deployments.
Common use casesโ
- Maintenance & Critical Events: Lock deployment during maintenance, peak traffic periods (campaigns, holidays), or critical events to maintain stability
- Emergency Situations: Lock deployment in emergencies such as security breaches or vulnerabilities to mitigate risks and prevent further issues
- Post-Incident Fixes: Unlock deployment to allow teams to implement necessary fixes or updates swiftly and restore system functionality
- Automated Compliance: Ensure deployment policies are enforced automatically through CI/CD pipelines
Prerequisitesโ
- Complete the onboarding process.
- Access to your Port organization with permissions to create blueprints and self-service actions.
- Port's GitHub app needs to be installed (required for GitHub workflow implementation).
- Slack webhook URL for notifications (optional).
Set up data modelโ
The service
blueprint that was created for you as part of the onboarding process will need to be extended with additional properties for managing lock states across different environments.
Update the service blueprint
-
Head to the data model page.
-
Click on the
Service
blueprint. -
Click on the
{...} Edit JSON
button. -
Copy and paste the following JSON configuration into the editor.
Additional properties to add (click to expand)
Add the following properties to the existing
properties
section of your service blueprint:"locked_in_prod": {
"icon": "DefaultProperty",
"title": "Locked in Prod",
"type": "boolean",
"default": false
},
"locked_reason_prod": {
"icon": "DefaultProperty",
"title": "Locked Reason Prod",
"type": "string"
},
"locked_in_test": {
"icon": "DefaultProperty",
"title": "Locked in Test",
"type": "boolean",
"default": false
},
"locked_reason_test": {
"icon": "DefaultProperty",
"title": "Locked Reason Test",
"type": "string"
},
"trigger_type": {
"icon": "DefaultProperty",
"title": "Lock or Unlock",
"type": "string"
},
"triggered_environment": {
"icon": "DefaultProperty",
"title": "Triggered Environment",
"type": "string"
} -
Click
Save
.
Implementationโ
Lock services without deployment checksโ
We will implement the service locking and unlocking functionality using Port's synced webhooks in self-service actions and an automation to update the service entities.
Follow these steps to set it up:
Create self-service actions
Follow these steps to create the lock and unlock actions:
-
Head to the self-service page.
-
Click on the
+ New Action
button. -
Click on the
{...} Edit JSON
button. -
Copy and paste the following JSON configuration for the lock action.
Lock Service action (click to expand)
{
"identifier": "lock_service",
"title": "Lock Service",
"icon": "Lock",
"description": "Lock service in Port",
"trigger": {
"type": "self-service",
"operation": "DAY-2",
"userInputs": {
"properties": {
"reason": {
"type": "string",
"title": "Reason"
},
"environment": {
"type": "string",
"title": "Environment",
"enum": [
"Production",
"Staging",
"Development"
],
"enumColors": {
"Production": "green",
"Staging": "orange",
"Development": "blue"
}
}
},
"required": ["reason", "environment"],
"order": [
"reason",
"environment"
]
},
"blueprintIdentifier": "service"
},
"invocationMethod": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "service",
"mapping": {
"identifier": "{{ .entity.identifier }}",
"title": "{{ .entity.title }}",
"properties": {
"{{ if .inputs.environment == 'Production' then 'locked_in_prod' else 'locked_in_test' end }}": true,
"{{ if .inputs.environment == 'Production' then 'locked_reason_prod' else 'locked_reason_test' end }}": "{{ .inputs.reason }}",
"trigger_type": "Locked",
"triggered_environment": "{{ .inputs.environment }}"
}
}
},
"requiredApproval": false
} -
Click
Save
. -
Create another action for unlocking services with the following JSON configuration:
Unlock service action (click to expand)
{
"identifier": "unlock_service",
"title": "Unlock Service",
"icon": "Unlock",
"description": "Unlock service in Port",
"trigger": {
"type": "self-service",
"operation": "DAY-2",
"userInputs": {
"properties": {
"reason": {
"type": "string",
"title": "Reason"
},
"environment": {
"type": "string",
"title": "Environment",
"enum": [
"Production",
"Staging",
"Development"
],
"enumColors": {
"Production": "green",
"Staging": "orange",
"Development": "blue"
}
}
},
"required": ["reason", "environment"],
"order": [
"reason",
"environment"
]
},
"blueprintIdentifier": "service"
},
"invocationMethod": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "service",
"mapping": {
"identifier": "{{ .entity.identifier }}",
"title": "{{ .entity.title }}",
"properties": {
"{{ if .inputs.environment == 'Production' then 'locked_in_prod' else 'locked_in_test' end }}": false,
"{{ if .inputs.environment == 'Production' then 'locked_reason_prod' else 'locked_reason_test' end }}": "{{ .inputs.reason }}",
"trigger_type": "Unlocked",
"triggered_environment": "{{ .inputs.environment }}"
}
}
},
"requiredApproval": false
} -
Click
Save
.
Set up Slack notifications
Create an automation that sends Slack notifications when service lock status changes.
-
Head to the automation page.
-
Click on the
+ Automation
button. -
Copy and paste the following JSON configuration into the editor.
Slack webhook URLReplace
<Your Generated Slack Webhook>
in the automation definition with your actual Slack webhook URL.Slack notification automation (click to expand)
{
"identifier": "serviceLockStatusChange",
"title": "Notify Slack on Service Lock Status Change",
"icon": "Slack",
"description": "Sends a Slack message when the service lock status changes.",
"trigger": {
"type": "automation",
"event": {
"type": "ENTITY_UPDATED",
"blueprintIdentifier": "service"
},
"condition": {
"type": "JQ",
"expressions": [
".diff.after.properties.locked_in_prod != .diff.before.properties.locked_in_prod or .diff.after.properties.locked_in_test != .diff.before.properties.locked_in_test"
],
"combinator": "or"
}
},
"invocationMethod": {
"type": "WEBHOOK",
"url": "<Your Generated Slack Webhook>",
"agent": false,
"synchronized": true,
"body": {
"text": "*Port Service {{ .event.diff.after.properties.trigger_type }}*\n\n*Service Name*: {{ .event.diff.after.title }}\n*Link*: https://app.getport.io/{{ .event.context.blueprintIdentifier }}Entity?identifier={{ .event.context.entityIdentifier }}\n\n*Environment:* {{ .event.diff.after.properties.triggered_environment }}\n*Reason:* {{ if .event.diff.after.properties.triggered_environment == 'Production' then .event.diff.after.properties.locked_reason_prod else .event.diff.after.properties.locked_reason_test end }}"
}
},
"publish": true
} -
Click
Save
.
Lock services with deployment checksโ
This option integrates with a CI/CD pipeline to check the lock status of a service before deployment.
We leverge on Port's Run Github workflow backend in the self-service action to perform the lock/unlock operation.
This approach provides comprehensive deployment protection by blocking builds when services are locked.
Follow these steps to set it up:
Add GitHub secrets
In your GitHub repository, go to Settings > Secrets and add the following secrets:
PORT_CLIENT_ID
- Your portclient id
How to get the credentials.PORT_CLIENT_SECRET
- Your portclient secret
How to get the credentials.
Add GitHub workflow
Create the file .github/workflows/check-service-lock.yml
in the .github/workflows
folder of your repository.
GitHub workflow configuration (click to expand)
name: Check Service Lock Status Before Deployment
on:
push:
branches:
- "main"
pull_request:
branches:
- "main"
types: [opened, synchronize, reopened]
jobs:
get-entity:
runs-on: ubuntu-latest
outputs:
entity: ${{ steps.port-github-action.outputs.entity }}
steps:
- id: port-github-action
name: Get entity from Port
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
baseUrl: https://api.getport.io
identifier: notification-service # Replace with your service identifier
blueprint: service
operation: GET
check-production-lock:
runs-on: ubuntu-latest
needs: get-entity
if: github.ref == 'refs/heads/main'
steps:
- name: Get production lock status
run: echo "PROD_LOCK_STATUS=$(echo '${{needs.get-entity.outputs.entity}}' | jq -r .properties.locked_in_prod)" >> $GITHUB_ENV
- name: Get production lock reason
run: echo "PROD_LOCK_REASON=$(echo '${{needs.get-entity.outputs.entity}}' | jq -r .properties.locked_reason_prod)" >> $GITHUB_ENV
- name: Check production lock status ๐ง
if: ${{ env.PROD_LOCK_STATUS == 'true' }}
run: |
echo "โ Service is locked in production"
echo "๐ Lock reason: ${{ env.PROD_LOCK_REASON }}"
echo "๐ Deployment blocked until service is unlocked"
exit 1
- name: Production lock check passed โ
if: ${{ env.PROD_LOCK_STATUS == 'false' }}
run: |
echo "โ
Service is not locked in production"
echo "๐ Ready to proceed with deployment"
check-test-lock:
runs-on: ubuntu-latest
needs: get-entity
if: github.event_name == 'pull_request'
steps:
- name: Get test environment lock status
run: echo "TEST_LOCK_STATUS=$(echo '${{needs.get-entity.outputs.entity}}' | jq -r .properties.locked_in_test)" >> $GITHUB_ENV
- name: Get test environment lock reason
run: echo "TEST_LOCK_REASON=$(echo '${{needs.get-entity.outputs.entity}}' | jq -r .properties.locked_reason_test)" >> $GITHUB_ENV
- name: Check test environment lock status ๐ง
if: ${{ env.TEST_LOCK_STATUS == 'true' }}
run: |
echo "โ Service is locked in test environment"
echo "๐ Lock reason: ${{ env.TEST_LOCK_REASON }}"
echo "๐ Test deployment blocked until service is unlocked"
exit 1
- name: Test environment lock check passed โ
if: ${{ env.TEST_LOCK_STATUS == 'false' }}
run: |
echo "โ
Service is not locked in test environment"
echo "๐ Ready to proceed with test deployment"
run-production-deployment:
runs-on: ubuntu-latest
needs: [check-production-lock]
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to Production ๐
run: |
echo "๐ Deploying service to production environment"
echo "โ
Production deployment completed successfully"
# Add your actual deployment commands here
run-test-deployment:
runs-on: ubuntu-latest
needs: [check-test-lock]
if: github.event_name == 'pull_request'
steps:
- name: Deploy to Test Environment ๐งช
run: |
echo "๐งช Deploying service to test environment"
echo "โ
Test deployment completed successfully"
# Add your actual test deployment commands here
The port_region
, port.baseUrl
, portBaseUrl
, port_base_url
and OCEAN__PORT__BASE_URL
parameters are used to select which instance or Port API will be used.
Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.
- If you use the EU region of Port (https://app.port.io), your API URL is
https://api.port.io
. - If you use the US region of Port (https://app.us.port.io), your API URL is
https://api.us.port.io
.
Let's test it!โ
-
Lock a service:
- Head to the self-service page and click on the
Lock Service
action. - Choose the service you want to lock and enter a reason (e.g., "Maintenance window for database upgrade")
- Select the environment (Production, Staging, or Development) and click
Execute
- Head to the self-service page and click on the
-
Verify the lock works:
- Check your Slack channel for the lock notification (if configured)
- If using the GitHub workflow approach, try pushing code to your repository - the workflow should fail with a lock message.
-
Test unlocking:
- Execute the
Unlock Service
action to unlock the service. - Check your Slack channel for the unlock notification and try pushing code again - the workflow should now succeed.
- Execute the
Here are some visual examples of the workflow in action:
When a service is locked:
If you try to push code to your repository when the locked_in_prod
field is set to true
, the deployment workflow will stop:

When you look at the step that failed, you will see that the failure is due to the value of the locked field:

When a service is unlocked:
If you set the value of the locked_in_prod
field to false
, the workflow will perform the deployment without any issue:

Slack notifications:
Below is the result of successful service lock and unlock alerts sent to a Slack channel after triggering the actions:

