diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 93c49a6c..06fd3e03 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -339,6 +339,7 @@ export default defineConfig({ { text: 'Rolling Updates', link: '/knowledge-base/rolling-updates' }, { text: 'Health Checks', link: '/knowledge-base/health-checks' }, { text: 'Cron Syntax', link: '/knowledge-base/cron-syntax' }, + { text: 'Service API Management', link: '/knowledge-base/services-api' }, ] }, { diff --git a/docs/.vitepress/theme/openapi.json b/docs/.vitepress/theme/openapi.json index 9d69c4ae..2a621fcd 100644 --- a/docs/.vitepress/theme/openapi.json +++ b/docs/.vitepress/theme/openapi.json @@ -8368,6 +8368,181 @@ ] } }, + "/services/{uuid}/applications": { + "get": { + "tags": ["Services"], + "summary": "List Applications", + "description": "List all applications within a service.", + "operationId": "list-service-applications", + "parameters": [ + { + "name": "uuid", + "in": "path", + "description": "UUID of the service.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "List of service applications.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ServiceApplication" + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/401" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/services/{uuid}/applications/{app_uuid}": { + "patch": { + "tags": ["Services"], + "summary": "Update Application", + "description": "Update a service application's configuration including FQDN, display name, and proxy settings. Changes require a service restart to take effect.", + "operationId": "update-service-application", + "parameters": [ + { + "name": "uuid", + "in": "path", + "description": "UUID of the service.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "app_uuid", + "in": "path", + "description": "UUID of the application.", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "description": "Fields to update on the service application.", + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "description": "Fully qualified domain name(s) with optional port. Multiple domains can be comma-separated (e.g., 'app.example.com:8080,app2.example.com').", + "example": "app.example.com:8080" + }, + "human_name": { + "type": "string", + "nullable": true, + "description": "Human-readable display name for the application.", + "example": "Production API Server" + }, + "description": { + "type": "string", + "nullable": true, + "description": "Application description.", + "example": "Production workflow automation server" + }, + "image": { + "type": "string", + "nullable": true, + "description": "Docker image for the application. WARNING: Changing this may corrupt application data.", + "example": "docker.n8n.io/n8nio/n8n:latest" + }, + "exclude_from_status": { + "type": "boolean", + "description": "Whether to exclude this application from service status monitoring.", + "example": false + }, + "is_log_drain_enabled": { + "type": "boolean", + "description": "Enable log drain for this application. Requires server-level log drain support.", + "example": false + }, + "is_gzip_enabled": { + "type": "boolean", + "description": "Enable gzip compression in the proxy.", + "example": true + }, + "is_stripprefix_enabled": { + "type": "boolean", + "description": "Enable path prefix stripping in the proxy.", + "example": true + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Application updated successfully.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ServiceApplication" + }, + { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Application updated successfully. Restart the service to apply changes." + } + } + } + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/401" + }, + "404": { + "$ref": "#/components/responses/404" + }, + "409": { + "$ref": "#/components/responses/409" + }, + "422": { + "$ref": "#/components/responses/422" + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, "/teams": { "get": { "tags": ["Teams"], @@ -9427,6 +9602,72 @@ }, "type": "object" }, + "ServiceApplication": { + "description": "Service Application model", + "properties": { + "uuid": { + "type": "string", + "description": "The unique identifier of the service application.", + "example": "app-uuid-123" + }, + "name": { + "type": "string", + "description": "The internal name of the application.", + "example": "n8n" + }, + "human_name": { + "type": "string", + "nullable": true, + "description": "The human-readable display name of the application.", + "example": "N8N Workflow Automation" + }, + "description": { + "type": "string", + "nullable": true, + "description": "The description of the application.", + "example": "Production workflow automation server" + }, + "fqdn": { + "type": "string", + "nullable": true, + "description": "The fully qualified domain name(s) with optional port. Multiple domains can be comma-separated.", + "example": "n8n.example.com:5678" + }, + "image": { + "type": "string", + "nullable": true, + "description": "The Docker image used by the application.", + "example": "docker.n8n.io/n8nio/n8n:latest" + }, + "status": { + "type": "string", + "nullable": true, + "description": "The current status of the application.", + "example": "running" + }, + "exclude_from_status": { + "type": "boolean", + "description": "Whether to exclude this application from service status monitoring.", + "example": false + }, + "is_log_drain_enabled": { + "type": "boolean", + "description": "Whether log drain is enabled for this application.", + "example": false + }, + "is_gzip_enabled": { + "type": "boolean", + "description": "Whether gzip compression is enabled in the proxy.", + "example": true + }, + "is_stripprefix_enabled": { + "type": "boolean", + "description": "Whether path prefix stripping is enabled in the proxy.", + "example": true + } + }, + "type": "object" + }, "Team": { "description": "Team model", "properties": { @@ -9564,6 +9805,42 @@ } } }, + "409": { + "description": "Domain conflict detected.", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "Domain conflict detected. The domain is already in use by another resource." + }, + "conflicts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "example": "app.example.com:8080" + }, + "resource_type": { + "type": "string", + "example": "application" + }, + "resource_uuid": { + "type": "string", + "example": "other-app-uuid" + } + } + } + } + }, + "type": "object" + } + } + } + }, "422": { "description": "Validation error.", "content": { diff --git a/docs/api-reference/authorization.md b/docs/api-reference/authorization.md index 3ff8af41..72ac519c 100644 --- a/docs/api-reference/authorization.md +++ b/docs/api-reference/authorization.md @@ -37,9 +37,11 @@ The token will only be able to access resources that are owned by the team that Some API data won't get returned if the API token doesn't have correct permissions ::: -Currently there are three types of permissions: +Currently there are several types of permissions: - read-only `(default)` +- read +- write - read:sensitive - view:sensitive - `*` (all permissions) @@ -48,6 +50,14 @@ Currently there are three types of permissions: With this permission, you can only read data from the API, but you can't create, update, or delete any resources. Also you can't see sensitive data. +### `read` + +With this permission, you can read data from the API including non-sensitive resource information. You cannot create, update, or delete any resources. + +### `write` + +With this permission, you can create, update, and delete resources via the API. This includes modifying service applications, updating configurations, and managing deployments. + ### `read:sensitive` With this permission, you can only read data from the API and see sensitive information that is normally redacted. You cannot create, update, or delete any resources. diff --git a/docs/knowledge-base/services-api.md b/docs/knowledge-base/services-api.md new file mode 100644 index 00000000..15b15166 --- /dev/null +++ b/docs/knowledge-base/services-api.md @@ -0,0 +1,371 @@ +--- +title: Managing Service Applications via API +description: Automate service application management with Coolify's REST API endpoints for listing and updating service applications programmatically. +--- + +# Managing Service Applications via API + +Coolify provides REST API endpoints to programmatically manage service applications, enabling automation and integration with your existing workflows. + +## Overview + +Two API endpoints allow you to manage service applications: + +1. **List Service Applications** - Retrieve all applications within a service +2. **Update Service Application** - Modify application configuration, domains, and proxy settings + +These endpoints enable you to automate tasks like domain updates, proxy configuration changes, and service management without using the web interface. + +## Prerequisites + +Before using these endpoints, you need: + +- **API Token**: Generate from `Keys & Tokens` → `API tokens` in the Coolify UI +- **Permissions**: + - `read` permission for listing applications + - `write` permission for updating applications +- **Service UUID**: Found in your service URL or via the services API +- **Application UUID**: Obtained from the list applications endpoint + +::: tip +Learn more about API authentication and permissions in the [Authorization](/api-reference/authorization) guide. +::: + +## List Service Applications + +Retrieve all applications within a specific service. + +### Endpoint + +``` +GET /api/v1/services/{service_uuid}/applications +``` + +### Authentication + +Requires a Bearer token with `read` permission. + +### Example Request + +```bash +curl -X GET "https://coolify.app/api/v1/services/{service-uuid}/applications" \ + -H "Authorization: Bearer {your-token}" +``` + +### Example Response + +```json +[ + { + "uuid": "app-uuid-123", + "name": "n8n", + "human_name": "N8N Workflow Automation", + "description": "Production workflow automation server", + "fqdn": "n8n.example.com:5678", + "image": "docker.n8n.io/n8nio/n8n:latest", + "status": "running", + "exclude_from_status": false, + "is_log_drain_enabled": false, + "is_gzip_enabled": true, + "is_stripprefix_enabled": true + } +] +``` + +### Response Fields + +| Field | Type | Description | +|-------|------|-------------| +| `uuid` | string | Unique identifier for the application | +| `name` | string | Application name (internal) | +| `human_name` | string | Human-readable display name | +| `description` | string | Application description | +| `fqdn` | string | Fully qualified domain name(s) with optional port | +| `image` | string | Docker image used by the application | +| `status` | string | Current application status | +| `exclude_from_status` | boolean | Whether to exclude from service status monitoring | +| `is_log_drain_enabled` | boolean | Whether log drain is enabled | +| `is_gzip_enabled` | boolean | Whether gzip compression is enabled in proxy | +| `is_stripprefix_enabled` | boolean | Whether path prefix stripping is enabled in proxy | + +## Update Service Application + +Modify an application's configuration, including domains, display name, and proxy settings. + +### Endpoint + +``` +PATCH /api/v1/services/{service_uuid}/applications/{app_uuid} +``` + +### Authentication + +Requires a Bearer token with `write` permission. + +### Updatable Fields + +| Field | Type | Description | +|-------|------|-------------| +| `fqdn` | string | Domain(s) with optional port. Multiple domains comma-separated. | +| `human_name` | string | Human-readable display name | +| `description` | string | Application description | +| `image` | string | Docker image (⚠️ WARNING: may corrupt data) | +| `exclude_from_status` | boolean | Exclude from service status monitoring | +| `is_log_drain_enabled` | boolean | Enable log drain (requires server support) | +| `is_gzip_enabled` | boolean | Enable gzip compression in proxy | +| `is_stripprefix_enabled` | boolean | Enable path prefix stripping in proxy | + +::: warning +Only the fields listed above can be updated. Any additional or unknown fields will result in a `422 Validation Error`. +::: + +### Update Single Domain + +```bash +curl -X PATCH "https://coolify.app/api/v1/services/{service-uuid}/applications/{app-uuid}" \ + -H "Authorization: Bearer {your-token}" \ + -H "Content-Type: application/json" \ + -d '{"fqdn": "newdomain.example.com:8080"}' +``` + +### Update Multiple Domains + +```bash +curl -X PATCH "https://coolify.app/api/v1/services/{service-uuid}/applications/{app-uuid}" \ + -H "Authorization: Bearer {your-token}" \ + -H "Content-Type: application/json" \ + -d '{"fqdn": "app1.example.com:8080,app2.example.com:9090"}' +``` + +### Update Display Name and Proxy Settings + +```bash +curl -X PATCH "https://coolify.app/api/v1/services/{service-uuid}/applications/{app-uuid}" \ + -H "Authorization: Bearer {your-token}" \ + -H "Content-Type: application/json" \ + -d '{ + "human_name": "Production API Server", + "is_gzip_enabled": false, + "is_stripprefix_enabled": false + }' +``` + +### Clear FQDN + +```bash +curl -X PATCH "https://coolify.app/api/v1/services/{service-uuid}/applications/{app-uuid}" \ + -H "Authorization: Bearer {your-token}" \ + -H "Content-Type: application/json" \ + -d '{"fqdn": ""}' +``` + +### Example Response + +```json +{ + "uuid": "app-uuid-123", + "name": "n8n", + "human_name": "Production API Server", + "description": "Production web application", + "fqdn": "app.example.com:8080", + "image": "nginx:latest", + "exclude_from_status": false, + "is_log_drain_enabled": false, + "is_gzip_enabled": false, + "is_stripprefix_enabled": false, + "message": "Application updated successfully. Restart the service to apply changes." +} +``` + +## Features + +### FQDN Validation + +The API automatically validates and normalizes domain names: + +- Validates domain format (supports ports) +- Converts to lowercase +- Removes duplicate domains +- Trims whitespace and commas + +**Supported Formats:** +- Single domain: `app.example.com` +- With port: `app.example.com:8080` +- Multiple domains: `app1.example.com,app2.example.com` +- Mixed ports: `app1.example.com:8080,app2.example.com:9090` + +### Domain Conflict Detection + +Before updating a domain, Coolify checks if it's already in use by another resource. If a conflict is detected, you'll receive a `409 Conflict` response with details about the existing resource. + +### Auto-Regeneration + +When certain fields are modified, Coolify automatically regenerates the docker-compose configuration: + +- `fqdn` +- `human_name` +- `image` +- `is_gzip_enabled` +- `is_stripprefix_enabled` + +::: danger Service Restart Required +Configuration changes do not take effect immediately. You **must restart the service** for changes to apply. + +Use the service restart endpoint: +```bash +POST /api/v1/services/{service_uuid}/restart +``` +::: + +## Error Responses + +### 401 Unauthorized + +Invalid or missing API token. + +```json +{ + "message": "Unauthenticated." +} +``` + +### 404 Not Found + +Service or application not found. + +```json +{ + "message": "Service not found." +} +``` + +or + +```json +{ + "message": "Application not found." +} +``` + +### 409 Conflict + +Domain already in use by another resource. + +```json +{ + "message": "Domain conflict detected. The domain is already in use by another resource.", + "conflicts": [ + { + "fqdn": "app.example.com:8080", + "resource_type": "application", + "resource_uuid": "other-app-uuid" + } + ] +} +``` + +### 422 Validation Error + +Invalid field values or unknown fields. + +```json +{ + "message": "Validation failed.", + "errors": { + "fqdn": ["The provided domain format is invalid."], + "invalid_field": ["This field is not allowed."] + } +} +``` + +**Log Drain Error:** + +```json +{ + "message": "Log drain is not enabled on the server.", + "errors": { + "is_log_drain_enabled": [ + "Log drain must be enabled on the server before enabling it on the application." + ] + } +} +``` + +## Best Practices + +### 1. Always Restart After Updates + +Changes to service applications require a service restart to take effect. Plan your updates accordingly and restart the service after making changes. + +### 2. Check for Domain Conflicts + +Before updating domains, verify they're not already in use. The API will reject conflicting domains with a `409` error. + +### 3. Use Log Drain Carefully + +Log drain requires server-level support. Ensure your server has log drain enabled before enabling it on individual applications. + +### 4. Be Cautious with Image Updates + +⚠️ Changing the Docker image can corrupt application data if the new image is incompatible. Only update images if you understand the implications and have backups. + +### 5. Test in Development First + +Always test configuration changes in a development environment before applying them to production services. + +### 6. Field Whitelisting + +Only the 8 documented fields can be updated. Attempting to modify other fields will result in a validation error. This is a security feature to prevent unintended changes. + +## Use Cases + +### Automated Domain Management + +Script domain updates across multiple services when migrating infrastructure or reorganizing your deployment structure. + +### CI/CD Integration + +Integrate service application management into your deployment pipelines, automatically updating configurations as part of your release process. + +### Monitoring Integration + +Exclude specific applications from status monitoring during maintenance windows or for applications with known issues. + +### Proxy Configuration Management + +Centrally manage proxy settings like gzip compression and path prefix stripping across all your service applications. + +## Example Workflow + +Here's a complete workflow for updating a service application: + +```bash +# 1. List all applications in a service +APPS=$(curl -s -X GET "https://coolify.app/api/v1/services/{service-uuid}/applications" \ + -H "Authorization: Bearer {your-token}") + +echo "$APPS" | jq '.' + +# 2. Extract the application UUID you want to update +APP_UUID=$(echo "$APPS" | jq -r '.[0].uuid') + +# 3. Update the application +curl -X PATCH "https://coolify.app/api/v1/services/{service-uuid}/applications/${APP_UUID}" \ + -H "Authorization: Bearer {your-token}" \ + -H "Content-Type: application/json" \ + -d '{ + "fqdn": "new-domain.example.com", + "human_name": "Updated Application Name", + "is_gzip_enabled": true + }' + +# 4. Restart the service to apply changes +curl -X POST "https://coolify.app/api/v1/services/{service-uuid}/restart" \ + -H "Authorization: Bearer {your-token}" +``` + +## Additional Resources + +- [API Authorization Guide](/api-reference/authorization) +- [Complete API Reference](/api-reference/api) +- [Services Overview](/services/introduction) diff --git a/docs/public/openapi.yml b/docs/public/openapi.yml index 97739a07..f02bd13a 100644 --- a/docs/public/openapi.yml +++ b/docs/public/openapi.yml @@ -5902,6 +5902,125 @@ paths: $ref: "#/components/responses/404" security: - bearerAuth: [] + "/services/{uuid}/applications": + get: + tags: + - Services + summary: List Applications + description: "List all applications within a service." + operationId: list-service-applications + parameters: + - name: uuid + in: path + description: "UUID of the service." + required: true + schema: + type: string + format: uuid + responses: + "200": + description: "List of service applications." + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ServiceApplication" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + security: + - bearerAuth: [] + "/services/{uuid}/applications/{app_uuid}": + patch: + tags: + - Services + summary: Update Application + description: "Update a service application's configuration including FQDN, display name, and proxy settings. Changes require a service restart to take effect." + operationId: update-service-application + parameters: + - name: uuid + in: path + description: "UUID of the service." + required: true + schema: + type: string + format: uuid + - name: app_uuid + in: path + description: "UUID of the application." + required: true + schema: + type: string + format: uuid + requestBody: + description: "Fields to update on the service application." + required: true + content: + application/json: + schema: + type: object + properties: + fqdn: + type: string + nullable: true + description: "Fully qualified domain name(s) with optional port. Multiple domains can be comma-separated (e.g., 'app.example.com:8080,app2.example.com')." + example: "app.example.com:8080" + human_name: + type: string + nullable: true + description: "Human-readable display name for the application." + example: "Production API Server" + description: + type: string + nullable: true + description: "Application description." + example: "Production workflow automation server" + image: + type: string + nullable: true + description: "Docker image for the application. WARNING: Changing this may corrupt application data." + example: "docker.n8n.io/n8nio/n8n:latest" + exclude_from_status: + type: boolean + description: "Whether to exclude this application from service status monitoring." + example: false + is_log_drain_enabled: + type: boolean + description: "Enable log drain for this application. Requires server-level log drain support." + example: false + is_gzip_enabled: + type: boolean + description: "Enable gzip compression in the proxy." + example: true + is_stripprefix_enabled: + type: boolean + description: "Enable path prefix stripping in the proxy." + example: true + responses: + "200": + description: "Application updated successfully." + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/ServiceApplication" + - type: object + properties: + message: + type: string + example: "Application updated successfully. Restart the service to apply changes." + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "422": + $ref: "#/components/responses/422" + security: + - bearerAuth: [] /teams: get: tags: @@ -6669,6 +6788,59 @@ components: type: string description: "The date and time when the service was deleted." type: object + ServiceApplication: + description: "Service Application model" + properties: + uuid: + type: string + description: "The unique identifier of the service application." + example: "app-uuid-123" + name: + type: string + description: "The internal name of the application." + example: "n8n" + human_name: + type: string + nullable: true + description: "The human-readable display name of the application." + example: "N8N Workflow Automation" + description: + type: string + nullable: true + description: "The description of the application." + example: "Production workflow automation server" + fqdn: + type: string + nullable: true + description: "The fully qualified domain name(s) with optional port. Multiple domains can be comma-separated." + example: "n8n.example.com:5678" + image: + type: string + nullable: true + description: "The Docker image used by the application." + example: "docker.n8n.io/n8nio/n8n:latest" + status: + type: string + nullable: true + description: "The current status of the application." + example: "running" + exclude_from_status: + type: boolean + description: "Whether to exclude this application from service status monitoring." + example: false + is_log_drain_enabled: + type: boolean + description: "Whether log drain is enabled for this application." + example: false + is_gzip_enabled: + type: boolean + description: "Whether gzip compression is enabled in the proxy." + example: true + is_stripprefix_enabled: + type: boolean + description: "Whether path prefix stripping is enabled in the proxy." + example: true + type: object Team: description: "Team model" properties: @@ -6764,6 +6936,30 @@ components: type: string example: "Resource not found." type: object + "409": + description: "Domain conflict detected." + content: + application/json: + schema: + properties: + message: + type: string + example: "Domain conflict detected. The domain is already in use by another resource." + conflicts: + type: array + items: + type: object + properties: + fqdn: + type: string + example: "app.example.com:8080" + resource_type: + type: string + example: "application" + resource_uuid: + type: string + example: "other-app-uuid" + type: object "422": description: "Validation error." content: diff --git a/docs/services/introduction.md b/docs/services/introduction.md index d89234ad..de3e4114 100644 --- a/docs/services/introduction.md +++ b/docs/services/introduction.md @@ -63,6 +63,12 @@ You can help by: Learn how to contribute new services in our [contribution guide](/get-started/contribute/service). +## Managing Services via API + +Service applications can be managed programmatically through Coolify's REST API. You can list applications within a service and update their configuration including domains, proxy settings, and more. + +Learn more in the [Service API Management guide](/knowledge-base/services-api). + --- Ready to explore what's available? Check out [all services](/services/overview) in our library.