@@ -104,18 +103,18 @@ print(response.json())
```javascript
const response = await fetch(
- "https://api.plane.so/api/v1/workspaces/my-workspace/projects/project-uuid/work-items/work-item-uuid/worklogs/",
+ 'https://api.plane.so/api/v1/workspaces/my-workspace/projects/project-uuid/work-items/work-item-uuid/worklogs/',
{
- method: "POST",
+ method: 'POST',
headers: {
- "X-API-Key": "your-api-key",
- "Content-Type": "application/json"
+ 'X-API-Key': 'your-api-key',
+ 'Content-Type': 'application/json',
},
body: JSON.stringify({
- "description": "example-description",
- "duration": 1
-})
- }
+ description: 'example-description',
+ duration: 1,
+ }),
+ },
);
const data = await response.json();
```
diff --git a/docs/api-reference/worklogs/delete-worklog.md b/docs/api-reference/worklogs/delete-worklog.md
index 8abee01a..3fd50d9f 100644
--- a/docs/api-reference/worklogs/delete-worklog.md
+++ b/docs/api-reference/worklogs/delete-worklog.md
@@ -4,7 +4,6 @@ description: Delete a worklog via Plane API. HTTP DELETE request for removing re
keywords: plane, plane api, rest api, api integration, time tracking, worklogs, time management
---
-
# Delete a worklog
diff --git a/docs/api-reference/worklogs/update-worklog.md b/docs/api-reference/worklogs/update-worklog.md
index 720b8d1d..40d195d9 100644
--- a/docs/api-reference/worklogs/update-worklog.md
+++ b/docs/api-reference/worklogs/update-worklog.md
@@ -4,7 +4,6 @@ description: Update a worklog via Plane API. HTTP PATCH request format, editable
keywords: plane, plane api, rest api, api integration, time tracking, worklogs, time management
---
-
# Update a worklog
@@ -110,18 +109,18 @@ print(response.json())
```javascript
const response = await fetch(
- "https://api.plane.so/api/v1/workspaces/my-workspace/projects/project-uuid/work-items/work-item-uuid/worklogs/{worklog_id}/",
+ 'https://api.plane.so/api/v1/workspaces/my-workspace/projects/project-uuid/work-items/work-item-uuid/worklogs/{worklog_id}/',
{
- method: "PATCH",
+ method: 'PATCH',
headers: {
- "X-API-Key": "your-api-key",
- "Content-Type": "application/json"
+ 'X-API-Key': 'your-api-key',
+ 'Content-Type': 'application/json',
},
body: JSON.stringify({
- "description": "example-description",
- "duration": 1
-})
- }
+ description: 'example-description',
+ duration: 1,
+ }),
+ },
);
const data = await response.json();
```
diff --git a/docs/dev-tools/agents/best-practices.md b/docs/dev-tools/agents/best-practices.md
index 53782976..dcc12bf3 100644
--- a/docs/dev-tools/agents/best-practices.md
+++ b/docs/dev-tools/agents/best-practices.md
@@ -34,7 +34,10 @@ Send a `thought` activity within the first few seconds of receiving a webhook:
```typescript
import { PlaneClient } from '@makeplane/plane-node-sdk';
-async function handleWebhook(webhook: AgentRunActivityWebhook, credentials: { bot_token: string; workspace_slug: string }) {
+async function handleWebhook(
+ webhook: AgentRunActivityWebhook,
+ credentials: { bot_token: string; workspace_slug: string },
+) {
const planeClient = new PlaneClient({
baseUrl: process.env.PLANE_API_URL || 'https://api.plane.so',
accessToken: credentials.bot_token,
@@ -85,6 +88,7 @@ def handle_webhook(webhook: dict, credentials: dict):
# ... rest of the logic
```
+
:::
### Thought activity best practices
@@ -94,13 +98,15 @@ def handle_webhook(webhook: dict, credentials: dict):
- Use thoughts to explain what the agent is doing, not technical details
**Good examples:**
+
- "Analyzing your question about project timelines..."
- "Searching for relevant work items..."
- "Preparing response with the requested data..."
**Avoid:**
+
- "Initializing LLM context with temperature 0.7..."
-- "Executing database query SELECT * FROM..."
+- "Executing database query SELECT \* FROM..."
- Generic messages like "Working..." repeated multiple times
## Acknowledging important signals
@@ -119,7 +125,10 @@ When a user wants to stop an agent run, Plane sends a `stop` signal with the act
== TypeScript {#typescript}
```typescript
-async function handleWebhook(webhook: AgentRunActivityWebhook, credentials: { bot_token: string; workspace_slug: string }) {
+async function handleWebhook(
+ webhook: AgentRunActivityWebhook,
+ credentials: { bot_token: string; workspace_slug: string },
+) {
const planeClient = new PlaneClient({
baseUrl: process.env.PLANE_API_URL || 'https://api.plane.so',
accessToken: credentials.bot_token,
@@ -183,14 +192,15 @@ def handle_webhook(webhook: dict, credentials: dict):
# Continue with normal processing...
```
+
:::
### Signal considerations
-| Signal | How to Handle |
-|--------|---------------|
+| Signal | How to Handle |
+| ---------- | ----------------------------------------- |
| `continue` | Default behavior, proceed with processing |
-| `stop` | Immediately halt and confirm |
+| `stop` | Immediately halt and confirm |
## Progress communication
@@ -202,17 +212,17 @@ When your agent performs multiple steps, send thought activities for each:
```typescript
// Step 1: Acknowledge
-await createThought("Understanding your request...");
+await createThought('Understanding your request...');
// Step 2: First action
-await createAction("searchDocuments", { query: userQuery });
+await createAction('searchDocuments', { query: userQuery });
const searchResults = await searchDocuments(userQuery);
// Step 3: Processing
-await createThought("Found relevant information. Analyzing...");
+await createThought('Found relevant information. Analyzing...');
// Step 4: Additional work
-await createAction("generateSummary", { data: searchResults });
+await createAction('generateSummary', { data: searchResults });
const summary = await generateSummary(searchResults);
// Step 5: Final response
@@ -235,7 +245,10 @@ Graceful error handling is crucial for a good user experience.
### Always catch and report errors
```typescript
-async function handleWebhook(webhook: AgentRunActivityWebhook, credentials: { bot_token: string; workspace_slug: string }) {
+async function handleWebhook(
+ webhook: AgentRunActivityWebhook,
+ credentials: { bot_token: string; workspace_slug: string },
+) {
const planeClient = new PlaneClient({
baseUrl: process.env.PLANE_API_URL || 'https://api.plane.so',
accessToken: credentials.bot_token,
@@ -256,7 +269,6 @@ async function handleWebhook(webhook: AgentRunActivityWebhook, credentials: { bo
type: 'response',
content: { type: 'response', body: result },
});
-
} catch (error) {
// ALWAYS inform the user about errors
await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentRunId, {
@@ -285,11 +297,13 @@ function getUserFriendlyErrorMessage(error: Error): string {
### Error message guidelines
**Do:**
+
- Use clear, non-technical language
- Suggest next steps when possible
- Be honest about what went wrong (at a high level)
**Don't:**
+
- Expose stack traces or technical details
- Blame the user for errors
- Leave users without any feedback
@@ -302,15 +316,12 @@ For multi-turn conversations, maintain context from previous activities.
```typescript
// Get all activities for context
-const activities = await planeClient.agentRuns.activities.list(
- credentials.workspace_slug,
- agentRunId
-);
+const activities = await planeClient.agentRuns.activities.list(credentials.workspace_slug, agentRunId);
// Build conversation history
const history = activities.results
- .filter(a => a.type === 'prompt' || a.type === 'response')
- .map(a => ({
+ .filter((a) => a.type === 'prompt' || a.type === 'response')
+ .map((a) => ({
role: a.type === 'prompt' ? 'user' : 'assistant',
content: a.content.body,
}));
@@ -339,7 +350,7 @@ async function longRunningTask(agentRunId: string) {
const HEARTBEAT_INTERVAL = 60000; // 1 minute
const heartbeat = setInterval(async () => {
- await createThought("Still working on your request...");
+ await createThought('Still working on your request...');
}, HEARTBEAT_INTERVAL);
try {
@@ -359,7 +370,7 @@ async function longRunningTask(agentRunId: string) {
```typescript
// Good: Respond immediately, process async
-app.post("/webhook", async (req, res) => {
+app.post('/webhook', async (req, res) => {
res.status(200).json({ received: true });
// Process in background
@@ -370,21 +381,25 @@ app.post("/webhook", async (req, res) => {
## Summary checklist
**Responsiveness**
+
- Send thought within seconds of webhook
- Return webhook response quickly
- Send heartbeats for long operations
**Signal handling**
+
- Always check for `stop` signal first
- Handle all signal types appropriately
- Confirm when stopping
**Error handling**
+
- Wrap processing in try/catch
- Always send error activity on failure
- Use friendly error messages
**User experience**
+
- Progress updates for long tasks
- Clear, non-technical communication
- Maintain conversation context
diff --git a/docs/dev-tools/agents/building-an-agent.md b/docs/dev-tools/agents/building-an-agent.md
index c1d3d059..5ca764b5 100644
--- a/docs/dev-tools/agents/building-an-agent.md
+++ b/docs/dev-tools/agents/building-an-agent.md
@@ -68,24 +68,24 @@ An **AgentRun** tracks a complete interaction session between a user and your ag
#### Key fields
-| Field | Type | Description |
-|-------|------|-------------|
-| `id` | UUID | Unique identifier for the agent run |
-| `agent_user` | UUID | The bot user ID representing your agent |
-| `issue` | UUID | The work item where the interaction started |
-| `project` | UUID | The project containing the work item |
-| `workspace` | UUID | The workspace where the agent is installed |
-| `comment` | UUID | The comment thread for this run |
-| `source_comment` | UUID | The original comment that triggered the run |
-| `creator` | UUID | The user who initiated the run |
-| `status` | String | Current status (`created`, `in_progress`, `awaiting`, `completed`, `stopping`, `stopped`, `failed`, `stale`) |
-| `started_at` | DateTime | When the run started |
-| `ended_at` | DateTime | When the run ended (if applicable) |
-| `stopped_at` | DateTime | When a stop was requested |
-| `stopped_by` | UUID | User who requested the stop |
-| `external_link` | URL | Optional link to external dashboard/logs |
-| `error_metadata` | JSON | Error details if the run failed |
-| `type` | String | Type of run (currently `comment_thread`) |
+| Field | Type | Description |
+| ---------------- | -------- | ------------------------------------------------------------------------------------------------------------ |
+| `id` | UUID | Unique identifier for the agent run |
+| `agent_user` | UUID | The bot user ID representing your agent |
+| `issue` | UUID | The work item where the interaction started |
+| `project` | UUID | The project containing the work item |
+| `workspace` | UUID | The workspace where the agent is installed |
+| `comment` | UUID | The comment thread for this run |
+| `source_comment` | UUID | The original comment that triggered the run |
+| `creator` | UUID | The user who initiated the run |
+| `status` | String | Current status (`created`, `in_progress`, `awaiting`, `completed`, `stopping`, `stopped`, `failed`, `stale`) |
+| `started_at` | DateTime | When the run started |
+| `ended_at` | DateTime | When the run ended (if applicable) |
+| `stopped_at` | DateTime | When a stop was requested |
+| `stopped_by` | UUID | User who requested the stop |
+| `external_link` | URL | Optional link to external dashboard/logs |
+| `error_metadata` | JSON | Error details if the run failed |
+| `type` | String | Type of run (currently `comment_thread`) |
### AgentRunActivity
@@ -93,18 +93,18 @@ An **AgentRunActivity** represents a single message or action within an Agent Ru
#### Key fields
-| Field | Type | Description |
-|-------|------|-------------|
-| `id` | UUID | Unique identifier for the activity |
-| `agent_run` | UUID | The parent Agent Run |
-| `type` | String | Activity type (`prompt`, `thought`, `action`, `response`, `elicitation`, `error`) |
-| `content` | JSON | The activity content (structure varies by type) |
-| `content_metadata` | JSON | Additional metadata about the content |
-| `ephemeral` | Boolean | If true, the activity is temporary and won't create a comment |
-| `signal` | String | Signal for how to handle the activity (`continue`, `stop`, `auth_request`, `select`) |
-| `signal_metadata` | JSON | Additional signal data |
-| `actor` | UUID | The user or bot that created the activity |
-| `comment` | UUID | Associated comment (for non-ephemeral activities) |
+| Field | Type | Description |
+| ------------------ | ------- | ------------------------------------------------------------------------------------ |
+| `id` | UUID | Unique identifier for the activity |
+| `agent_run` | UUID | The parent Agent Run |
+| `type` | String | Activity type (`prompt`, `thought`, `action`, `response`, `elicitation`, `error`) |
+| `content` | JSON | The activity content (structure varies by type) |
+| `content_metadata` | JSON | Additional metadata about the content |
+| `ephemeral` | Boolean | If true, the activity is temporary and won't create a comment |
+| `signal` | String | Signal for how to handle the activity (`continue`, `stop`, `auth_request`, `select`) |
+| `signal_metadata` | JSON | Additional signal data |
+| `actor` | UUID | The user or bot that created the activity |
+| `comment` | UUID | Associated comment (for non-ephemeral activities) |
### Creating activities
@@ -124,6 +124,7 @@ npm install @makeplane/plane-node-sdk
```bash
pip install plane-sdk
```
+
:::
#### Activity examples
@@ -267,6 +268,7 @@ plane_client.agent_runs.activities.create(
),
)
```
+
:::
### Content payload types
@@ -282,6 +284,7 @@ Internal reasoning from the agent. Automatically marked as ephemeral.
"body": "The user is asking about weather data for their location."
}
```
+
:::
::: details Action
@@ -310,6 +313,7 @@ With result:
}
}
```
+
:::
::: details Response
@@ -321,6 +325,7 @@ A final response to the user. Creates a comment reply.
"body": "Here's the weather forecast for San Francisco..."
}
```
+
:::
::: details Elicitation
@@ -332,6 +337,7 @@ A question requesting user input. Creates a comment and sets run to `awaiting`.
"body": "Could you please specify which date range you're interested in?"
}
```
+
:::
::: details Error
@@ -343,18 +349,19 @@ An error message. Creates a comment and sets run to `failed`.
"body": "I encountered an error while processing your request."
}
```
+
:::
### Signals
Signals provide additional context about how an activity should be interpreted:
-| Signal | Description |
-|--------|-------------|
-| `continue` | Default signal, indicates the conversation can continue |
-| `stop` | User requested to stop the agent run |
+| Signal | Description |
+| -------------- | --------------------------------------------------------- |
+| `continue` | Default signal, indicates the conversation can continue |
+| `stop` | User requested to stop the agent run |
| `auth_request` | Agent needs user to authenticate with an external service |
-| `select` | Agent is presenting options for user to select from |
+| `select` | Agent is presenting options for user to select from |
See [Signals & Content Payload](/dev-tools/agents/signals-content-payload) for detailed information.
@@ -363,6 +370,7 @@ See [Signals & Content Payload](/dev-tools/agents/signals-content-payload) for d
Activities with `ephemeral: true` are temporary and don't create comments. They're useful for showing agent progress without cluttering the conversation.
The following activity types are automatically marked as ephemeral:
+
- `thought`
- `action`
- `error`
@@ -474,7 +482,7 @@ interface AgentRunActivityWebhook {
async function handleWebhook(
webhook: AgentRunActivityWebhook,
- credentials: { bot_token: string; workspace_slug: string }
+ credentials: { bot_token: string; workspace_slug: string },
) {
// Only handle agent_run_activity webhooks
if (webhook.type !== 'agent_run_activity') {
@@ -572,6 +580,7 @@ def handle_webhook(webhook: dict, credentials: dict):
),
)
```
+
:::
## Next steps
diff --git a/docs/dev-tools/agents/overview.md b/docs/dev-tools/agents/overview.md
index d3ae9716..26820c6c 100644
--- a/docs/dev-tools/agents/overview.md
+++ b/docs/dev-tools/agents/overview.md
@@ -114,16 +114,16 @@ sequenceDiagram
Agent Runs transition through various states based on activities:
-| Status | Description |
-|--------|-------------|
-| `created` | The run has been initiated but not yet started processing |
-| `in_progress` | The agent is actively processing the request |
-| `awaiting` | The agent is waiting for additional input from the user |
-| `completed` | The agent has successfully finished processing |
-| `stopping` | A stop request has been received and is being processed |
-| `stopped` | The run has been successfully stopped |
-| `failed` | The run encountered an error and cannot continue |
-| `stale` | The run has not been updated in 5 minutes and is considered stale |
+| Status | Description |
+| ------------- | ----------------------------------------------------------------- |
+| `created` | The run has been initiated but not yet started processing |
+| `in_progress` | The agent is actively processing the request |
+| `awaiting` | The agent is waiting for additional input from the user |
+| `completed` | The agent has successfully finished processing |
+| `stopping` | A stop request has been received and is being processed |
+| `stopped` | The run has been successfully stopped |
+| `failed` | The run encountered an error and cannot continue |
+| `stale` | The run has not been updated in 5 minutes and is considered stale |
### Continuing a conversation
diff --git a/docs/dev-tools/agents/signals-content-payload.md b/docs/dev-tools/agents/signals-content-payload.md
index 5edacafd..1fc24c59 100644
--- a/docs/dev-tools/agents/signals-content-payload.md
+++ b/docs/dev-tools/agents/signals-content-payload.md
@@ -24,12 +24,12 @@ Signals are metadata that modify how an activity should be interpreted or handle
### Available signals
-| Signal | Description | Use Case |
-|--------|-------------|----------|
-| `continue` | Default signal, indicates normal flow | Standard responses, ongoing conversation |
-| `stop` | User requested to stop the agent | Cancellation, abort operations |
-| `auth_request` | Agent needs external authentication | OAuth flows, API key collection |
-| `select` | Agent presenting options for selection | Multiple choice questions |
+| Signal | Description | Use Case |
+| -------------- | -------------------------------------- | ---------------------------------------- |
+| `continue` | Default signal, indicates normal flow | Standard responses, ongoing conversation |
+| `stop` | User requested to stop the agent | Cancellation, abort operations |
+| `auth_request` | Agent needs external authentication | OAuth flows, API key collection |
+| `select` | Agent presenting options for selection | Multiple choice questions |
### Signal: `continue`
@@ -111,6 +111,7 @@ await planeClient.agentRuns.activities.create(credentials.workspace_slug, agentR
```
**Requirements:**
+
- The URL must start with `https://`
- The URL should be a secure endpoint on your agent's server
- After authentication, redirect the user back or notify completion
@@ -149,8 +150,8 @@ Internal reasoning or progress updates from the agent. Automatically marked as e
```typescript
interface ThoughtContent {
- type: "thought";
- body: string; // The thought message
+ type: 'thought';
+ body: string; // The thought message
}
```
@@ -195,9 +196,11 @@ plane_client.agent_runs.activities.create(
),
)
```
+
:::
**Best practices for thoughts:**
+
- Keep them concise and user-meaningful
- Use to show progress, not internal implementation
- Update as you move through stages of processing
@@ -210,9 +213,10 @@ Describes a tool invocation or external action. Automatically marked as ephemera
```typescript
interface ActionContent {
- type: "action";
- action: string; // Name of the tool/action
- parameters: { // Key-value pairs of parameters
+ type: 'action';
+ action: string; // Name of the tool/action
+ parameters: {
+ // Key-value pairs of parameters
[key: string]: string;
};
}
@@ -323,9 +327,11 @@ plane_client.agent_runs.activities.create(
),
)
```
+
:::
**Parameter requirements:**
+
- All parameter keys must be strings
- All parameter values must be strings
- Use `content_metadata` to store complex/structured data
@@ -338,8 +344,8 @@ A final response to the user. Creates a comment reply visible to users.
```typescript
interface ResponseContent {
- type: "response";
- body: string; // The response message (supports Markdown)
+ type: 'response';
+ body: string; // The response message (supports Markdown)
}
```
@@ -359,12 +365,12 @@ interface ResponseContent {
```typescript
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
- type: "response",
+ type: 'response',
content: {
- type: "response",
+ type: 'response',
body: "Here's the weather in San Francisco:\n\n**68°F** - Partly Cloudy\n\nExpect mild conditions throughout the day.",
},
- signal: "continue",
+ signal: 'continue',
});
```
@@ -386,9 +392,11 @@ plane_client.agent_runs.activities.create(
),
)
```
+
:::
**Response best practices:**
+
- Use Markdown for formatting
- Be clear and concise
- Include relevant context
@@ -402,8 +410,8 @@ Requests clarification or input from the user. Creates a comment and sets the Ag
```typescript
interface ElicitationContent {
- type: "elicitation";
- body: string; // The question or request (supports Markdown)
+ type: 'elicitation';
+ body: string; // The question or request (supports Markdown)
}
```
@@ -423,10 +431,10 @@ interface ElicitationContent {
```typescript
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
- type: "elicitation",
+ type: 'elicitation',
content: {
- type: "elicitation",
- body: "To generate the report, I need a few details:\n\n- What date range should I cover?\n- Should I include completed work items or only open ones?",
+ type: 'elicitation',
+ body: 'To generate the report, I need a few details:\n\n- What date range should I cover?\n- Should I include completed work items or only open ones?',
},
});
```
@@ -448,9 +456,11 @@ plane_client.agent_runs.activities.create(
),
)
```
+
:::
**Elicitation best practices:**
+
- Ask specific, answerable questions
- Provide options when possible
- Don't ask too many questions at once
@@ -464,8 +474,8 @@ Reports an error or failure. Creates a comment and sets the Agent Run status to
```typescript
interface ErrorContent {
- type: "error";
- body: string; // The error message (supports Markdown)
+ type: 'error';
+ body: string; // The error message (supports Markdown)
}
```
@@ -485,13 +495,13 @@ interface ErrorContent {
```typescript
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
- type: "error",
+ type: 'error',
content: {
- type: "error",
- body: "I was unable to access the GitHub repository. Please ensure the integration is properly configured.",
+ type: 'error',
+ body: 'I was unable to access the GitHub repository. Please ensure the integration is properly configured.',
},
signal_metadata: {
- error_code: "GITHUB_ACCESS_DENIED",
+ error_code: 'GITHUB_ACCESS_DENIED',
retryable: true,
},
});
@@ -518,9 +528,11 @@ plane_client.agent_runs.activities.create(
),
)
```
+
:::
**Error best practices:**
+
- Use friendly, non-technical language
- Suggest next steps when possible
- Store detailed error info in `signal_metadata`
@@ -534,8 +546,8 @@ This type is **user-generated only**. Your agent cannot create prompt activities
```typescript
interface PromptContent {
- type: "prompt";
- body: string; // The user's message
+ type: 'prompt';
+ body: string; // The user's message
}
```
@@ -555,6 +567,7 @@ Ephemeral activities are temporary and won't create comment replies. They're use
### Automatically ephemeral types
The following activity types are automatically marked as ephemeral:
+
- `thought`
- `action`
- `error`
@@ -593,21 +606,21 @@ Use `content_metadata` to store additional structured data about an activity:
```typescript
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
- type: "action",
+ type: 'action',
content: {
- type: "action",
- action: "analyzeCode",
+ type: 'action',
+ action: 'analyzeCode',
parameters: {
- file: "src/index.ts",
- result: "Found 3 potential issues",
+ file: 'src/index.ts',
+ result: 'Found 3 potential issues',
},
},
content_metadata: {
analysis_results: {
issues: [
- { line: 42, severity: "warning", message: "Unused variable" },
- { line: 78, severity: "error", message: "Type mismatch" },
- { line: 156, severity: "info", message: "Consider refactoring" },
+ { line: 42, severity: 'warning', message: 'Unused variable' },
+ { line: 78, severity: 'error', message: 'Type mismatch' },
+ { line: 156, severity: 'info', message: 'Consider refactoring' },
],
processing_time_ms: 1250,
},
@@ -646,6 +659,7 @@ plane_client.agent_runs.activities.create(
),
)
```
+
:::
## Signal metadata
@@ -703,45 +717,45 @@ const planeClient = new PlaneClient({
// 1. Thought - Show reasoning (ephemeral)
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
- type: "thought",
- content: { type: "thought", body: "Analyzing the request..." },
+ type: 'thought',
+ content: { type: 'thought', body: 'Analyzing the request...' },
});
// 2. Action - Tool invocation (ephemeral)
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
- type: "action",
+ type: 'action',
content: {
- type: "action",
- action: "searchWorkItems",
- parameters: { query: "bug", status: "open" },
+ type: 'action',
+ action: 'searchWorkItems',
+ parameters: { query: 'bug', status: 'open' },
},
});
// 3. Response - Final answer (creates comment)
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
- type: "response",
- content: { type: "response", body: "Found 5 open bugs." },
- signal: "continue",
+ type: 'response',
+ content: { type: 'response', body: 'Found 5 open bugs.' },
+ signal: 'continue',
});
// 4. Elicitation - Ask for input (creates comment, awaits response)
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
- type: "elicitation",
- content: { type: "elicitation", body: "Which bug should I prioritize?" },
- signal: "select",
+ type: 'elicitation',
+ content: { type: 'elicitation', body: 'Which bug should I prioritize?' },
+ signal: 'select',
signal_metadata: {
options: [
- { id: "bug-1", label: "AUTH-123: Login timeout" },
- { id: "bug-2", label: "API-456: Rate limiting" },
+ { id: 'bug-1', label: 'AUTH-123: Login timeout' },
+ { id: 'bug-2', label: 'API-456: Rate limiting' },
],
},
});
// 5. Error - Report failure (creates comment)
await planeClient.agentRuns.activities.create(workspaceSlug, agentRunId, {
- type: "error",
- content: { type: "error", body: "Unable to access the database." },
- signal_metadata: { error_code: "DB_CONNECTION_FAILED" },
+ type: 'error',
+ content: { type: 'error', body: 'Unable to access the database.' },
+ signal_metadata: { error_code: 'DB_CONNECTION_FAILED' },
});
```
@@ -819,6 +833,7 @@ plane_client.agent_runs.activities.create(
),
)
```
+
:::
## Next steps
diff --git a/docs/dev-tools/build-plane-app.md b/docs/dev-tools/build-plane-app.md
index 8fb3c6c9..9cdb51ff 100644
--- a/docs/dev-tools/build-plane-app.md
+++ b/docs/dev-tools/build-plane-app.md
@@ -19,12 +19,12 @@ Plane uses OAuth 2.0 to allow applications to access workspace data on behalf of
2. Click **Build your own**
3. Fill in the required details:
-| Field | Description |
-|-------|-------------|
-| **App Name** | Display name shown to users |
-| **Setup URL** | Entry point when users install your app. Your app redirects users to Plane's consent screen from here. |
-| **Redirect URI** | Callback URL where Plane sends users after they approve access, along with the authorization code. |
-| **Webhook URL** | Endpoint for receiving event notifications |
+| Field | Description |
+| ---------------- | ------------------------------------------------------------------------------------------------------ |
+| **App Name** | Display name shown to users |
+| **Setup URL** | Entry point when users install your app. Your app redirects users to Plane's consent screen from here. |
+| **Redirect URI** | Callback URL where Plane sends users after they approve access, along with the authorization code. |
+| **Webhook URL** | Endpoint for receiving event notifications |
4. For agents that respond to @mentions, enable **"Enable App Mentions"**
5. Save and store your **Client ID** and **Client Secret** securely
@@ -37,10 +37,10 @@ Never expose your Client Secret in client-side code or commit it to version cont
Plane supports two OAuth flows:
-| Flow | Use When | Token Type |
-|------|----------|------------|
-| **Bot Token** (Client Credentials) | Agents, webhooks, automation, background tasks | `bot_token` |
-| **User Token** (Authorization Code) | Actions on behalf of a specific user | `access_token` |
+| Flow | Use When | Token Type |
+| ----------------------------------- | ---------------------------------------------- | -------------- |
+| **Bot Token** (Client Credentials) | Agents, webhooks, automation, background tasks | `bot_token` |
+| **User Token** (Authorization Code) | Actions on behalf of a specific user | `access_token` |
::: info
Most integrations should use the **Bot Token flow**. Use User Token only when you need to perform actions as a specific user.
@@ -83,10 +83,10 @@ GET https://api.plane.so/auth/o/authorize-app/
After the user approves, Plane redirects to your Redirect URI with:
-| Parameter | Description |
-|-----------|-------------|
-| `app_installation_id` | Unique identifier for this installation |
-| `code` | Authorization code (not used in bot flow) |
+| Parameter | Description |
+| --------------------- | ----------------------------------------- |
+| `app_installation_id` | Unique identifier for this installation |
+| `code` | Authorization code (not used in bot flow) |
### 3. Exchange for Bot Token
@@ -188,10 +188,10 @@ Include a random `state` parameter to prevent CSRF attacks. Verify it matches wh
After approval, Plane redirects to your Redirect URI with:
-| Parameter | Description |
-|-----------|-------------|
-| `code` | Authorization code to exchange for tokens |
-| `state` | Your state parameter (verify this matches) |
+| Parameter | Description |
+| --------- | ------------------------------------------ |
+| `code` | Authorization code to exchange for tokens |
+| `state` | Your state parameter (verify this matches) |
### 3. Exchange Code for Tokens
@@ -250,11 +250,11 @@ When events occur in Plane, webhooks are sent to your Webhook URL.
### Webhook Headers
-| Header | Description |
-|--------|-------------|
-| `X-Plane-Delivery` | Unique delivery ID |
-| `X-Plane-Event` | Event type (e.g., `issue`, `issue_comment`) |
-| `X-Plane-Signature` | HMAC-SHA256 signature for verification |
+| Header | Description |
+| ------------------- | ------------------------------------------- |
+| `X-Plane-Delivery` | Unique delivery ID |
+| `X-Plane-Event` | Event type (e.g., `issue`, `issue_comment`) |
+| `X-Plane-Signature` | HMAC-SHA256 signature for verification |
### Verify Signature
@@ -282,6 +282,7 @@ function verifySignature(payload: string, signature: string, secret: string): bo
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
```
+
:::
### Webhook Payload
@@ -323,10 +324,10 @@ Free ngrok URLs change on restart. Update your app settings when the URL changes
Official SDKs provide OAuth helpers and typed API clients:
-| Language | Package |
-|----------|---------|
-| Node.js | [@makeplane/plane-node-sdk](https://www.npmjs.com/package/@makeplane/plane-node-sdk) |
-| Python | [plane-sdk](https://pypi.org/project/plane-sdk/) |
+| Language | Package |
+| -------- | ------------------------------------------------------------------------------------ |
+| Node.js | [@makeplane/plane-node-sdk](https://www.npmjs.com/package/@makeplane/plane-node-sdk) |
+| Python | [plane-sdk](https://pypi.org/project/plane-sdk/) |
```bash
npm install @makeplane/plane-node-sdk
@@ -337,6 +338,7 @@ pip install plane-sdk
::: details SDK OAuth Helper Methods
**Node.js:**
+
```typescript
import { OAuthClient } from '@makeplane/plane-node-sdk';
@@ -360,6 +362,7 @@ const newToken = await oauth.getRefreshToken(refreshToken);
```
**Python:**
+
```python
from plane.client import OAuthClient
@@ -412,11 +415,14 @@ const WEBHOOK_SECRET = process.env.PLANE_WEBHOOK_SECRET!;
const PLANE_API_URL = process.env.PLANE_API_URL || 'https://api.plane.so';
// In-memory storage (use a database in production)
-const installations = new Map
();
+const installations = new Map<
+ string,
+ {
+ botToken: string;
+ workspaceSlug: string;
+ appInstallationId: string;
+ }
+>();
// Setup URL - redirect to Plane's consent screen
app.get('/oauth/setup', (req, res) => {
@@ -448,19 +454,18 @@ app.get('/oauth/callback', async (req, res) => {
}).toString(),
{
headers: {
- 'Authorization': `Basic ${basicAuth}`,
+ Authorization: `Basic ${basicAuth}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
- }
+ },
);
const botToken = tokenRes.data.access_token;
// Get workspace details
- const installRes = await axios.get(
- `${PLANE_API_URL}/auth/o/app-installation/?id=${appInstallationId}`,
- { headers: { 'Authorization': `Bearer ${botToken}` } }
- );
+ const installRes = await axios.get(`${PLANE_API_URL}/auth/o/app-installation/?id=${appInstallationId}`, {
+ headers: { Authorization: `Bearer ${botToken}` },
+ });
const installation = installRes.data[0];
const workspaceId = installation.workspace;
@@ -471,7 +476,6 @@ app.get('/oauth/callback', async (req, res) => {
console.log(`Installed in workspace: ${workspaceSlug}`);
res.send('Installation successful! You can close this window.');
-
} catch (error) {
console.error('OAuth error:', error);
res.status(500).send('Installation failed');
diff --git a/docs/dev-tools/intro-webhooks.md b/docs/dev-tools/intro-webhooks.md
index 853ec380..a2083a3e 100644
--- a/docs/dev-tools/intro-webhooks.md
+++ b/docs/dev-tools/intro-webhooks.md
@@ -4,10 +4,10 @@ description: Configure webhooks for Plane. Setup real-time event notifications a
keywords: plane, developer tools, integrations, extensions, webhooks, automation, events
---
-
# Webhooks
A webhook triggers a HTTP POST request on the specified url, whenever there is a change in an event. Like a new project is created, updated or deleted then a webhook can be triggered to receive the required payload.
+
## Creating a webhook
`url` You are required to provide a url in which you want the payloads to be triggered.
@@ -25,13 +25,11 @@ After you create the webhook, a secret key will be created automatically and wil
```
-
-|Header |Description |
-|-----------------|--------------------------------------------------------------------|
-|X-Plane-Delivery |It is a randomly generated UUID for uniquely identifying the payload|
-|X-Plane-Event |It describes the event for which the webhook triggered |
-|X-Plane-Signature|A signature is generated based on the secret and the payload |
-
+| Header | Description |
+| ----------------- | -------------------------------------------------------------------- |
+| X-Plane-Delivery | It is a randomly generated UUID for uniquely identifying the payload |
+| X-Plane-Event | It describes the event for which the webhook triggered |
+| X-Plane-Signature | A signature is generated based on the secret and the payload |
### Webhook Payload Example for the project `update`
@@ -76,7 +74,6 @@ After you create the webhook, a secret key will be created automatically and wil
```
-
User can choose the things for which they want the webhook to be triggered.
Currently Plane supports the following events for which webhook can be trigged:
@@ -105,19 +102,18 @@ if not hmac.compare_digest(expected_signature, received_signature):
```
-
## How webhook works
Your webhook consumer is a simple HTTP endpoint. It must satisfy the following conditions:
-* It's available in a publicly accessible non-localhost URL.
-* It will respond to the Plane Webhook push (HTTP POST request) with a `HTTP 200` ("OK") response.
+- It's available in a publicly accessible non-localhost URL.
+- It will respond to the Plane Webhook push (HTTP POST request) with a `HTTP 200` ("OK") response.
If a delivery fails (i.e. server unavailable or responded with a non-200 HTTP status code), the push will be retried a couple of times. Here an exponential backoff delay is used: the attempt will be retried after approximately 10 minutes, then 30 minutes, and so on.
The webhooks are triggered for POST, PATCH, and DELETE requests.
-* For DELETE requests, the response only includes the ID of the deleted entity.
+- For DELETE requests, the response only includes the ID of the deleted entity.
```
"action":"delete",
@@ -130,8 +126,7 @@ The webhooks are triggered for POST, PATCH, and DELETE requests.
```
-
-* However, for both POST and PATCH requests, the complete payload is sent in the response.
+- However, for both POST and PATCH requests, the complete payload is sent in the response.
```
"event":"issue",
@@ -142,7 +137,6 @@ The webhooks are triggered for POST, PATCH, and DELETE requests.
```
-
::: info
Whenever an issue is added to the module, the corresponding issue webhook will be triggered. Similarly, any updates made to the cycle issue will also activate the issue webhook.
-:::
\ No newline at end of file
+:::
diff --git a/docs/dev-tools/mcp-server.md b/docs/dev-tools/mcp-server.md
index 2bed7212..867d8a63 100644
--- a/docs/dev-tools/mcp-server.md
+++ b/docs/dev-tools/mcp-server.md
@@ -4,7 +4,6 @@ description: Setup MCP Server for Plane. Integrate Plane with Model Context Prot
keywords: plane, developer tools, integrations, extensions, mcp server, protocol, integration
---
-
# MCP server
The [Model Context Protocol](https://modelcontextprotocol.io/overview) (MCP) is a
@@ -26,12 +25,12 @@ applications. Please send any issues to support@plane.so.
The Plane MCP Server supports multiple transport methods to accommodate different deployment scenarios:
-| Transport | Best for | Authentication |
-|-----------|----------|----------------|
-| [HTTP with OAuth](#remote-http-with-oauth) | Cloud users, simplest setup | Browser-based OAuth |
-| [HTTP with PAT Token](#remote-http-with-pat-token) | Automated workflows, CI/CD | API key in headers |
-| [Local Stdio](#local-stdio-transport) | Self-hosted Plane instances | Environment variables |
-| [SSE (Legacy)](#sse-transport-legacy) | Existing integrations | Browser-based OAuth |
+| Transport | Best for | Authentication |
+| -------------------------------------------------- | --------------------------- | --------------------- |
+| [HTTP with OAuth](#remote-http-with-oauth) | Cloud users, simplest setup | Browser-based OAuth |
+| [HTTP with PAT Token](#remote-http-with-pat-token) | Automated workflows, CI/CD | API key in headers |
+| [Local Stdio](#local-stdio-transport) | Self-hosted Plane instances | Environment variables |
+| [SSE (Legacy)](#sse-transport-legacy) | Existing integrations | Browser-based OAuth |
## Remote HTTP with OAuth
@@ -169,6 +168,7 @@ Use this method to connect to a self-hosted Plane instance. The Stdio transport
- **uvx**: Comes bundled with [uv](https://docs.astral.sh/uv/getting-started/installation/)
You can verify your installation by running:
+
```bash
python --version
uvx --version
@@ -194,11 +194,11 @@ uvx --version
### Environment variables
-| Variable | Required | Description |
-|----------|----------|-------------|
-| `PLANE_API_KEY` | Yes | Your Plane API key |
-| `PLANE_WORKSPACE_SLUG` | Yes | Your workspace slug |
-| `PLANE_BASE_URL` | No | API URL for self-hosted instances (defaults to `https://api.plane.so`) |
+| Variable | Required | Description |
+| ---------------------- | -------- | ---------------------------------------------------------------------- |
+| `PLANE_API_KEY` | Yes | Your Plane API key |
+| `PLANE_WORKSPACE_SLUG` | Yes | Your workspace slug |
+| `PLANE_BASE_URL` | No | API URL for self-hosted instances (defaults to `https://api.plane.so`) |
## SSE transport (Legacy)
@@ -283,4 +283,4 @@ If you continue to experience issues:
## Congrats!
-You have successfully connected your Plane workspace to the MCP server!
\ No newline at end of file
+You have successfully connected your Plane workspace to the MCP server!
diff --git a/docs/index.md b/docs/index.md
index be784dcb..9025f495 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -50,15 +50,17 @@ features:
### Deploy with Docker
+
Get Plane running in minutes with Docker Compose
-[Docker Compose](/self-hosting/methods/docker-compose)
+[Docker Compose](/self-hosting/methods/docker-compose)
### Configure Your Instance
+
Set up authentication and connect external services to your Plane deployment.
[Instance Admin](/self-hosting/govern/instance-admin) • [Configure SSO](/self-hosting/govern/authentication)
@@ -68,6 +70,7 @@ Set up authentication and connect external services to your Plane deployment.
### Manage instance
+
Keep your instance up-to-date with the latest features and security patches.
[Update guide](/self-hosting/manage/upgrade-plane) • [Manage licenses](/self-hosting/manage/manage-licenses/activate-pro-and-business)
diff --git a/docs/plane-one/governance/authentication/custom-sso.md b/docs/plane-one/governance/authentication/custom-sso.md
index 6286b330..bd660dea 100644
--- a/docs/plane-one/governance/authentication/custom-sso.md
+++ b/docs/plane-one/governance/authentication/custom-sso.md
@@ -4,7 +4,6 @@ description: OIDC and SAML. Complete guide and documentation for Plane.
keywords: plane
---
-
# OIDC and SAML
Plane enables custom SSO via any identity provider with an official and supported implementation of OIDC + SAML standards. This page cites examples from Okta, but we will soon publish provider-specific instructions in phases.
@@ -18,21 +17,21 @@ You will need to configure values on your IdP first and then on Plane later.
Create a Plane client or application per your IdP's documentation and configure ↓.
::: tip
- `domain.tld` is the domain that you have hosted your Plane app on.
+`domain.tld` is the domain that you have hosted your Plane app on.
:::
-| **Config** | **Key** |
-|----------------|-------------------------------------------------------|
-| Origin URL | `http(s)://domain.tld/auth/oidc/` |
-| Callback URL | `http(s)://domain.tld/auth/oidc/callback/` |
-| Logout URL | `http(s)://domain.tld/auth/oidc/logout/` |
+| **Config** | **Key** |
+| ------------ | ------------------------------------------ |
+| Origin URL | `http(s)://domain.tld/auth/oidc/` |
+| Callback URL | `http(s)://domain.tld/auth/oidc/callback/` |
+| Logout URL | `http(s)://domain.tld/auth/oidc/logout/` |
### On Plane
Go to `/god-mode/authentication/oidc` on your Plane app and find the configs ↓.
::: tip
- Your IdP will generate some of the following configs for you. Others, you will specify yourself. Just copy them over to each field.
+Your IdP will generate some of the following configs for you. Others, you will specify yourself. Just copy them over to each field.
:::

@@ -59,7 +58,7 @@ Go to `/god-mode/authentication/oidc` on your Plane app and find the configs ↓
To test if this URL is right, see if clicking the `Login with
` button brings up your IdP's authentication screen.
- 
+ 
- Finally, choose a name for your IdP on Plane so you can recognize this set of configs.
@@ -67,18 +66,18 @@ Go to `/god-mode/authentication/oidc` on your Plane app and find the configs ↓
You will need to configure values on your IdP first and then on Plane later.
::: tip
- `domain.tld` is the domain that you have hosted your Plane app on.
+`domain.tld` is the domain that you have hosted your Plane app on.
:::
### On your preferred IdP
Create a Plane client or application per your IdP's documentation and configure ↓.
-| **Config** | **Value** |
-|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|
-| Entity ID
Metadata that identifies Plane as an authorized service on your IdP | `http(s)://domain.tld/auth/oidc/` |
-| ACS URL
Assertion Consumer service that your IdP will redirect to after successful authentication by a user
This is roughly the counterpart of the `Callback URL` in OIDC set-ups. | `http(s)://domain.tld/auth/oidc/callback/`
Plane supports HTTP-POST bindings. |
-| SLS URL
Single Logout Service that your IdP will recognize to end a Plane session when a user logs out
This is roughly the counterpart of the `Logout URL` in OIDC set-ups. | `http(s)://domain.tld/auth/oidc/logout/` |
+| **Config** | **Value** |
+| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
+| Entity ID
Metadata that identifies Plane as an authorized service on your IdP | `http(s)://domain.tld/auth/oidc/` |
+| ACS URL
Assertion Consumer service that your IdP will redirect to after successful authentication by a user
This is roughly the counterpart of the `Callback URL` in OIDC set-ups. | `http(s)://domain.tld/auth/oidc/callback/`
Plane supports HTTP-POST bindings. |
+| SLS URL
Single Logout Service that your IdP will recognize to end a Plane session when a user logs out
This is roughly the counterpart of the `Logout URL` in OIDC set-ups. | `http(s)://domain.tld/auth/oidc/logout/` |
::: tip
When setting these values up on the IdP, it’s important to remember Plane does not need to provide a signing certificate like other service providers.
@@ -86,21 +85,22 @@ When setting these values up on the IdP, it’s important to remember Plane does
### Let your IdP identify your users on Plane.
-| **Config** | **Value** |
-|----------------------------|--------------------------------------------------------|
-| Name ID format | emailAddress
By default, your IdP should send back a username, but Plane recognizes email addresses as the username. Set the value to the above so Plane recognizes the user correctly.
+| **Config** | **Value** |
+| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Name ID format | emailAddress
By default, your IdP should send back a username, but Plane recognizes email addresses as the username. Set the value to the above so Plane recognizes the user correctly. |
+
### Set additional attribute values.
By default, your IdP will send the value listed under `Property`. You have to map it to the SAML attribute Plane recognizes.
-| **Default property value** | **Plane SAML attribute** |
-|----------------------------|--------------------------------------------|
-| user.firstName | first_name |
-| user.lastName | last_name |
-| user.email | email |
+| **Default property value** | **Plane SAML attribute** |
+| -------------------------- | ------------------------ |
+| user.firstName | first_name |
+| user.lastName | last_name |
+| user.email | email |
::: tip
- Depending on your IdP, you will have to find both the `Name ID format` and the three other user identification properties on different screens. Please refer to your IdP's documentation when configuring these up on your IdP. Additionally, you may have to configure the IdP to sign assertions. Irrespective of that, you have to copy the signing certificate from the IdP.
+Depending on your IdP, you will have to find both the `Name ID format` and the three other user identification properties on different screens. Please refer to your IdP's documentation when configuring these up on your IdP. Additionally, you may have to configure the IdP to sign assertions. Irrespective of that, you have to copy the signing certificate from the IdP.
:::
### On Plane
@@ -108,7 +108,7 @@ By default, your IdP will send the value listed under `Property`. You have to ma

::: tip
- You will find all of the values for the fields below in the `/metadata` endpoint your IdP generates for the Plane app or client.
+You will find all of the values for the fields below in the `/metadata` endpoint your IdP generates for the Plane app or client.
:::
- Copy the `ENTITY_ID` for the Plane client or app you just created over from your IdP and paste it in the field for it.
@@ -125,4 +125,4 @@ By default, your IdP will send the value listed under `Property`. You have to ma

-- Finally, paste the signing certificate from your IdP that you got in the last step of setting up your Plane client or app on your IdP above and paste it in the field for it.
\ No newline at end of file
+- Finally, paste the signing certificate from your IdP that you got in the last step of setting up your Plane client or app on your IdP above and paste it in the field for it.
diff --git a/docs/plane-one/governance/workspaces-and-teams.md b/docs/plane-one/governance/workspaces-and-teams.md
index 9ae48148..d1319eaf 100644
--- a/docs/plane-one/governance/workspaces-and-teams.md
+++ b/docs/plane-one/governance/workspaces-and-teams.md
@@ -4,7 +4,6 @@ description: Workspaces and teams. Complete guide and documentation for Plane.
keywords: plane
---
-
# Workspaces and teams
Since April 15 when Plane One was announced, we have communicated on our website and Discord that One will have one workspace linked to one domain and one license.
@@ -41,4 +40,5 @@ This unlocks new ways of using workspaces. For example, you could have a One-lic
To answer an obvious question, we can’t turn existing workspaces into the new license-linked workspaces retroactively because of how we were architected in the past. We can only do that going forward.
---
-For questions about this, please reach out to us on [Discord](https://discord.com/channels/1031547764020084846/1240291034307497994).
\ No newline at end of file
+
+For questions about this, please reach out to us on [Discord](https://discord.com/channels/1031547764020084846/1240291034307497994).
diff --git a/docs/plane-one/introduction.md b/docs/plane-one/introduction.md
index a7b9b2ad..f00b5679 100644
--- a/docs/plane-one/introduction.md
+++ b/docs/plane-one/introduction.md
@@ -4,12 +4,12 @@ description: Meet Plane One. Complete guide and documentation for Plane.
keywords: plane
---
-
# Meet Plane One
[Plane One](https://plane.so/one) is our first licensed self-hosted edition for growing teams serious about staying in control. One unlocks security, governance, and project management features scale-ups need to manage their instance and projects better.
## Good to know
+
1. Plane One is a self-hosted-only solution. To use our hosted Cloud editions, [go here](https://app.plane.so).
2. Plane One only works with a domain, not with an IP addresses or `localhost`.
3. Plane One comes with updates for two years with an option to auto-update to latest versions when they are released.
@@ -24,4 +24,5 @@ When you log in, you will see a list of your licenses. Clicking any will show yo
::: tipIf you are yet to get a license, do that [here](https://plane.so/one).:::
## Quickstart
-Plane One installs with a single command on your CLI. [Learn more](/self-hosting/methods/docker-compose).
\ No newline at end of file
+
+Plane One installs with a single command on your CLI. [Learn more](/self-hosting/methods/docker-compose).
diff --git a/docs/plane-one/manage/advanced-deploy.md b/docs/plane-one/manage/advanced-deploy.md
index 6900cb78..d858417c 100644
--- a/docs/plane-one/manage/advanced-deploy.md
+++ b/docs/plane-one/manage/advanced-deploy.md
@@ -4,6 +4,4 @@ description: Documentation. Complete guide and documentation for Plane.
keywords: plane
---
-
# Documentation
-
diff --git a/docs/plane-one/manage/prime-cli.md b/docs/plane-one/manage/prime-cli.md
index e0e52f7c..94161383 100644
--- a/docs/plane-one/manage/prime-cli.md
+++ b/docs/plane-one/manage/prime-cli.md
@@ -4,28 +4,27 @@ description: Manage your instance. Complete guide and documentation for Plane.
keywords: plane
---
-
# Manage your instance
::: warningUpdate your CLI with the command `sudo prime-cli update-cli` before you download any Plane One updates. The latest version of the CLI ensures your Plane upgrades happen smoothly.:::
The Prime CLI is our own command-line interface to help you install, upgrade, and manage your instance without being a pro at Docker. -
-Bring up the Prime CLI with ```sudo prime-cli``` from any directory on your machine.
+Bring up the Prime CLI with `sudo prime-cli` from any directory on your machine.
- The three operators you will use the most are,
+ - `start`
+
+ You will use this to start a service in the Docker network with the name of the service.
+
+ - `stop`
- - `start`
-
- You will use this to start a service in the Docker network with the name of the service.
+ You will use this to stop a service in the Docker network with the name of the service.
- - `stop`
-
- You will use this to stop a service in the Docker network with the name of the service.
+ - `restart`
+
+ You will use this to restart a service in the Docker network with the name of the service as a `{param or flag}`.
- - `restart`
-
- You will use this to restart a service in the Docker network with the name of the service as a `{param or flag}`.
- Often, you will want to monitor the health of your instance and see if some services are up or down. Use `monitor` to do that.
- `healthcheck` is another useful utility that lets you see the status and errors, if any, of all running services
@@ -33,62 +32,56 @@ Bring up the Prime CLI with ```sudo prime-cli``` from any directory on your mach
- `repair` fixes your Plane install for common errors automatically.
- `update-cli` downloads and installs the latest version of Prime CLI.
- ::: tip
- It is highly recommend to run this first before you download any Plane One updates. The latest version of the CLI ensures your Plane upgrades happen smoothly.
- :::
+ ::: tip
+ It is highly recommend to run this first before you download any Plane One updates. The latest version of the CLI ensures your Plane upgrades happen smoothly.
+ :::
+For more advanced admins that want greater control over their instance, the list of additional commands available on Prime CLI follow. - `configure`
-For more advanced admins that want greater control over their instance, the list of additional commands available on Prime CLI follow.
- - `configure`
-
Brings up a step form to let you specify the following.
::: details Steps to configure your instance
- `Listening port`
-
+
Specify the port that the built-in reverse proxy will use
Default value: 80
- `Max file-upload size`
-
+
Specify a size in MBs for how big each file uploaded to your Plane app can be
Default value: 5 MB
- `External Postgres URL`
-
+
Specify the URL of your own hosted Postgres if you would like to change the database your Plane app uses.
-
+
Default database: Postgres 15.5 in the Docker container
- `External Redis URL`
-
+
Specify the URL of your own hosted REdis if you would like to change the default Redis Plane ships with.
Default Redis: Redis 7.2.4
- `External storage`
-
+
Specify your AWS S3 bucket's credentials in the format below to change storage from the default Plane ships with.
- AWS Access Key ID
- AWS Secret Access Key
- AWS S3 Bucket Name
-
+
Default storage: MinIO
-
- - Confirm your choices on the screen ↓.
+
+ - Confirm your choices on the screen ↓.
This restarts your instance with the new configs.
:::
-- `upgrade`
+- `upgrade`
- checks your instance for available version upgrades and asks you for a confirmation before downloading the latest available version.
- 1. Typing `YES` lets the CLI automatically download's the latest version and installs it. Then it restarts the instance to load the latest app.
- 2. Typing `NO` cancels the upgrade.
+ checks your instance for available version upgrades and asks you for a confirmation before downloading the latest available version. 1. Typing `YES` lets the CLI automatically download's the latest version and installs it. Then it restarts the instance to load the latest app. 2. Typing `NO` cancels the upgrade.
-- `uninstall`
+- `uninstall`
- uninstalls Plane. Before it goes through, it asks you for a confirmation.
- 1. Typing `YES` lets the CLI clean up the `/opt/plane` folder, leaving behind the `/opt/plane/data` and `/opt/plane/logs` folders.
- 2. Typing `NO` cancels the uninstall.
\ No newline at end of file
+ uninstalls Plane. Before it goes through, it asks you for a confirmation. 1. Typing `YES` lets the CLI clean up the `/opt/plane` folder, leaving behind the `/opt/plane/data` and `/opt/plane/logs` folders. 2. Typing `NO` cancels the uninstall.
diff --git a/docs/plane-one/manage/prime-client.md b/docs/plane-one/manage/prime-client.md
index 4fd8329d..a1add335 100644
--- a/docs/plane-one/manage/prime-client.md
+++ b/docs/plane-one/manage/prime-client.md
@@ -4,7 +4,6 @@ description: Manage your license. Complete guide and documentation for Plane.
keywords: plane
---
-
# Manage your license
After you purchase Plane One, you can log in to our customer portal(https://prime.plane.so) and find your license in the portal.
@@ -27,4 +26,4 @@ Once you select a license from the list, you quickly get information on how to s
## Buy a new plane one license
-Within the customer portal, you can buy a new license for Plane One directly and self-host a new instance of Plane One.
\ No newline at end of file
+Within the customer portal, you can buy a new license for Plane One directly and self-host a new instance of Plane One.
diff --git a/docs/plane-one/self-host/guides.md b/docs/plane-one/self-host/guides.md
index 6900cb78..d858417c 100644
--- a/docs/plane-one/self-host/guides.md
+++ b/docs/plane-one/self-host/guides.md
@@ -4,6 +4,4 @@ description: Documentation. Complete guide and documentation for Plane.
keywords: plane
---
-
# Documentation
-
diff --git a/docs/plane-one/self-host/methods/docker.md b/docs/plane-one/self-host/methods/docker.md
index 1e0fdefd..4cd278bb 100644
--- a/docs/plane-one/self-host/methods/docker.md
+++ b/docs/plane-one/self-host/methods/docker.md
@@ -4,95 +4,105 @@ description: Docker. Complete guide and documentation for Plane.
keywords: plane
---
-
# Docker
Plane One supports a standard Docker-based installation via our own command-line interface called the Prime CLI.
+
## Pre-requisites
+
- A Plane One license
-If you don't have a license, [get it here](https://plane.so/one).
+ If you don't have a license, [get it here](https://plane.so/one).
- A virtual or on-prem machine with at-least 2 vCPUs and 4 GB RAM
- `x64` AKA `AMD 64` or `AArch 64` AKA `ARM 64` CPUs
- Ubuntu, Debian, or CentOS
- For Amazon Linux 2 or Linux 2023, click here.
+ For Amazon Linux 2 or Linux 2023, click here.
## Upgrading from Community Edition to One
-::: warning
+
+::: warning
Do not install One first if you are upgrading from the Community Edition. Follow the instructions below first all the way to the end of the section `Restoring your data to your One instance`.
We strongly recommend you install One on a fresh machine, not the one running Plane Community.
:::
### Backing up your data
+
`setup.sh` in the Community Edition now comes with an option to back up your data.
-1. Use `./setup.sh` to open the operator menu and select `7` to backup your Plane data.
+1. Use `./setup.sh` to open the operator menu and select `7` to backup your Plane data.
2. When done, your data will be backed up to the folder shown on the screen.
-e.g., `/plane-selfhost/plane-app/backup/20240522-1027`
+ e.g., `/plane-selfhost/plane-app/backup/20240522-1027`
3. This folder will contain 3 `tar.gz` files.
- - `pgdata.tar.gz`
- - `redisdata.tar.gz`
- - `uploads.tar.gz`
-4. Copy all the 3 files from the server running the Community Edition to any folder on the server running Plane One on any folder of your choice._createMdxContent
+ - `pgdata.tar.gz`
+ - `redisdata.tar.gz`
+ - `uploads.tar.gz`
+4. Copy all the 3 files from the server running the Community Edition to any folder on the server running Plane One on any folder of your choice.\_createMdxContent
e.g., `~/ce-backup`
### Restoring your data to your One instance
+
1. Start any command-line interface like Terminal and go into the folder with the back-up files.
- ```
- cd ~/ce-backup
- ```
+ ```
+ cd ~/ce-backup
+ ```
2. Copy and paste the script below on Terminal and hit Enter.
- ```
-
- TARGET_DIR=/opt/plane/data
- sudo mkdir -p $TARGET_DIR
- for FILE in *.tar.gz; do
- if [ -e "$FILE" ]; then
- tar -xzvf "$FILE" -C "$TARGET_DIR"
- else
- echo "No .tar.gz files found in the current directory."
- exit 1
- fi
- done
-
- mv $TARGET_DIR/pgdata/ $TARGET_DIR/db
- mv $TARGET_DIR/redisdata/ $TARGET_DIR/redis
- mkdir -p $TARGET_DIR/minio
- mv $TARGET_DIR/uploads/ $TARGET_DIR/minio/uploads/
- ```
+
+ ```
+
+ TARGET_DIR=/opt/plane/data
+ sudo mkdir -p $TARGET_DIR
+ for FILE in *.tar.gz; do
+ if [ -e "$FILE" ]; then
+ tar -xzvf "$FILE" -C "$TARGET_DIR"
+ else
+ echo "No .tar.gz files found in the current directory."
+ exit 1
+ fi
+ done
+
+ mv $TARGET_DIR/pgdata/ $TARGET_DIR/db
+ mv $TARGET_DIR/redisdata/ $TARGET_DIR/redis
+ mkdir -p $TARGET_DIR/minio
+ mv $TARGET_DIR/uploads/ $TARGET_DIR/minio/uploads/
+ ```
+
3. This script will extract your Community Edition data and restore it to /opt/plane/data.
4. Follow the instructions for new installations below.
## New installations
+
1. `ssh` into your machine as the root user (or user with sudo access) per the norms of your hosting provider.
2. Run the command below with the last twelve characters being your license key.
-::: tipThe easiest way to get this command is from the [customer portal](https://prime.plane.so/) where you also got your license key. Each entry on the portal shows the command bound to the license key, so you can just copy it from there.:::
+ ::: tipThe easiest way to get this command is from the [customer portal](https://prime.plane.so/) where you also got your license key. Each entry on the portal shows the command bound to the license key, so you can just copy it from there.:::
```
curl -fsSL https://prime.plane.so/install/YOUR-LICENSE-KEY | sh -
```
-
+
Successfully running that command,
+
1. Validates the license key
2. Downloads `prime-cli` per your OS and your CPU's architecture and saves it to the `/usr/bin` directory.
3. Runs the `prime-cli setup` command with necessary parameters. which in turn
- 1. Asks you for the domain to link the license to
+ 1. Asks you for the domain to link the license to
- Choose this carefully. The domain linked to the license can't be changed later.
+ Choose this carefully. The domain linked to the license can't be changed later.
- 2. Asks you to setup Plane in Express or Advanced modes
+ 2. Asks you to setup Plane in Express or Advanced modes
- Express is the fastest way to setup Plane. Advanced lets you configure all Docker services to your specs.
+ Express is the fastest way to setup Plane. Advanced lets you configure all Docker services to your specs.
- 3. Installs Docker and its necessary plugins
- 4. Downloads the license file
- 5. Downloads `docker-compose`, `plane.env`, and the images for the microservices that will run in the Docker network
- 6. Downloads the required docker images
- 7. Starts Plane services
+ 3. Installs Docker and its necessary plugins
+ 4. Downloads the license file
+ 5. Downloads `docker-compose`, `plane.env`, and the images for the microservices that will run in the Docker network
+ 6. Downloads the required docker images
+ 7. Starts Plane services
Successful installation shows you the domain you entered in `1.` above. Go to that domain to access your Plane app.
---
+
# Docker Swarm
-::: infoDocker Swarm will come soon. Please [reach out to us](mailto:support@plane.so) for updates and to have a quick chat to help us build it better.:::
\ No newline at end of file
+
+::: infoDocker Swarm will come soon. Please [reach out to us](mailto:support@plane.so) for updates and to have a quick chat to help us build it better.:::
diff --git a/docs/plane-one/self-host/methods/kubernetes.md b/docs/plane-one/self-host/methods/kubernetes.md
index 4243dcad..be67300f 100644
--- a/docs/plane-one/self-host/methods/kubernetes.md
+++ b/docs/plane-one/self-host/methods/kubernetes.md
@@ -4,13 +4,12 @@ description: Kubernetes. Complete guide and documentation for Plane.
keywords: plane
---
-
# Kubernetes
## Pre-requisite
- A Plane One license
-::: tip If you don’t have a license, get it [here](https://prime.plane.so/) :::
+ ::: tip If you don’t have a license, get it [here](https://prime.plane.so/) :::
- A working Kubernetes cluster
- `kubectl` and `helm` on the client system that you will use to install our Helm charts
@@ -19,237 +18,238 @@ keywords: plane
1. Open Terminal or any other command-line app that has access to Kubernetes tools on your local system.
2. Set the following environment variables.
- Copy the format of constants below, paste it on Terminal to start setting environment variables, set values for each variable, and hit ENTER or RETURN.
- ::: tip You will get the values for the variables from [prime.plane.so](https://prime.plane.so) under the Kuberntes tab of your license's details page. When installing Plane One for the first time, remember to specify a domain name.:::
+ Copy the format of constants below, paste it on Terminal to start setting environment variables, set values for each variable, and hit ENTER or RETURN.
+ ::: tip You will get the values for the variables from [prime.plane.so](https://prime.plane.so) under the Kuberntes tab of your license's details page. When installing Plane One for the first time, remember to specify a domain name.:::
- ```bash
- LICENSE_KEY=
- REG_USER_ID=
- REG_PASSWORD=<******>
- PLANE_VERSION=
- DOMAIN_NAME=
- ```
+ ```bash
+ LICENSE_KEY=
+ REG_USER_ID=
+ REG_PASSWORD=<******>
+ PLANE_VERSION=
+ DOMAIN_NAME=
+ ```
3. Add Plane helm chart repo
- Continue to be on the same Terminal window as with the previous steps, copy the code below, paste it on Terminal, and hit ENTER or RETURN.
+ Continue to be on the same Terminal window as with the previous steps, copy the code below, paste it on Terminal, and hit ENTER or RETURN.
- ```bash
- helm repo add plane https://helm.plane.so/
- helm repo update
- ```
+ ```bash
+ helm repo add plane https://helm.plane.so/
+ helm repo update
+ ```
4. Set-up and customization
- - Quick set-up
-
- This is the fastest way to deploy Plane with default settings. This will create stateful deployments for Postgres, Redis, and Minio with a persistent volume claim using the `longhorn` storage class. This also sets up the ingress routes for you using `nginx` ingress class.
- ::: tip To customize this, see `Custom ingress routes` below.:::
-
- Continue to be on the same Terminal window as you have so far, copy the code below, and paste it on your Terminal screen.
-
- ```bash
- helm install one-app plane/plane-enterprise \
- --create-namespace \
- --namespace plane-one \
- --set dockerRegistry.loginid=${REG_USER_ID} \
- --set dockerRegistry.password=${REG_PASSWORD} \
- --set license.licenseKey=${LICENSE_KEY} \
- --set license.licenseDomain=${DOMAIN_NAME} \
- --set license.licenseServer=https://prime.plane.so \
- --set planeVersion=${PLANE_VERSION} \
- --set ingress.ingressClass=nginx \
- --set env.storageClass=longhorn \
- --timeout 10m \
- --wait \
- --wait-for-jobs
- ```
-
- ::: tip
- This is the minimum required to set up Plane One. You can change the default namespace from `plane-one`, the default appname
- from `one-app`, the default storage class from `env.storageClass`, and the default ingress class from `ingress.ingressClass` to
- whatever you would like to.
-
- You can also pass other settings referring to `Configuration Settings` section.
- :::
- - Advance set-up
-
- For more control over your set-up, run the script below to download the `values.yaml` file and and edit using any editor like Vim or Nano.
-
- ```bash
- helm show values plane/plane-enterprise > values.yaml
- vi values.yaml
- ```
-
- Make sure you set the minimum required values as below.
- - `planeVersion: `
- - `dockerRegistry.loginid: `
- - `dockerRegistry.password: `
- - `license.licenseKey: `
- - `license.licenseDomain: `
- - `license.licenseServer: https://prime.plane.so`
- - `ingress.ingressClass: `
- - `env.storageClass: `
-
- ::: tip See `Available customizations` for more details.:::
-
- After saving the `values.yaml` file, continue to be on the same Terminal window as on the previous steps, copy the code below, and paste it on your Terminal screen.
-
- ```bash
- helm install one-app plane/plane-enterprise \
- --create-namespace \
- --namespace plane-one \
- -f values.yaml \
- --timeout 10m \
- --wait \
- --wait-for-jobs
- ```
+ - Quick set-up
+
+ This is the fastest way to deploy Plane with default settings. This will create stateful deployments for Postgres, Redis, and Minio with a persistent volume claim using the `longhorn` storage class. This also sets up the ingress routes for you using `nginx` ingress class.
+ ::: tip To customize this, see `Custom ingress routes` below.:::
+
+ Continue to be on the same Terminal window as you have so far, copy the code below, and paste it on your Terminal screen.
+
+ ```bash
+ helm install one-app plane/plane-enterprise \
+ --create-namespace \
+ --namespace plane-one \
+ --set dockerRegistry.loginid=${REG_USER_ID} \
+ --set dockerRegistry.password=${REG_PASSWORD} \
+ --set license.licenseKey=${LICENSE_KEY} \
+ --set license.licenseDomain=${DOMAIN_NAME} \
+ --set license.licenseServer=https://prime.plane.so \
+ --set planeVersion=${PLANE_VERSION} \
+ --set ingress.ingressClass=nginx \
+ --set env.storageClass=longhorn \
+ --timeout 10m \
+ --wait \
+ --wait-for-jobs
+ ```
+
+ ::: tip
+ This is the minimum required to set up Plane One. You can change the default namespace from `plane-one`, the default appname
+ from `one-app`, the default storage class from `env.storageClass`, and the default ingress class from `ingress.ingressClass` to
+ whatever you would like to.
+
+ You can also pass other settings referring to `Configuration Settings` section.
+ :::
+
+ - Advance set-up
+
+ For more control over your set-up, run the script below to download the `values.yaml` file and and edit using any editor like Vim or Nano.
+
+ ```bash
+ helm show values plane/plane-enterprise > values.yaml
+ vi values.yaml
+ ```
+
+ Make sure you set the minimum required values as below.
+ - `planeVersion: `
+ - `dockerRegistry.loginid: `
+ - `dockerRegistry.password: `
+ - `license.licenseKey: `
+ - `license.licenseDomain: `
+ - `license.licenseServer: https://prime.plane.so`
+ - `ingress.ingressClass: `
+ - `env.storageClass: `
+
+ ::: tip See `Available customizations` for more details.:::
+
+ After saving the `values.yaml` file, continue to be on the same Terminal window as on the previous steps, copy the code below, and paste it on your Terminal screen.
+
+ ```bash
+ helm install one-app plane/plane-enterprise \
+ --create-namespace \
+ --namespace plane-one \
+ -f values.yaml \
+ --timeout 10m \
+ --wait \
+ --wait-for-jobs
+ ```
## Available customizations
### Docker registry
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| dockerRegistry.enabled | true | Yes | Plane uses a private Docker registry which needs authenticated login. This must be set to `true` to install Plane One. |
-| dockerRegistry.registry | registry.plane.tools| Yes | The host that will serve the required Docker images; Don't change this. |
-| dockerRegistry.loginid | | Yes | Sets the `loginid` for the Docker registry. This is the same as the REG_USER_ID value on prime. plane.so |
-| dockerRegistry.password | | Yes | Sets the `password` for the Docker registry. This is the same as the REG_PASSWORD value on prime.plane.so|
+| Setting | Default | Required | Description |
+| ----------------------- | :------------------: | :------: | ---------------------------------------------------------------------------------------------------------------------- |
+| dockerRegistry.enabled | true | Yes | Plane uses a private Docker registry which needs authenticated login. This must be set to `true` to install Plane One. |
+| dockerRegistry.registry | registry.plane.tools | Yes | The host that will serve the required Docker images; Don't change this. |
+| dockerRegistry.loginid | | Yes | Sets the `loginid` for the Docker registry. This is the same as the REG_USER_ID value on prime. plane.so |
+| dockerRegistry.password | | Yes | Sets the `password` for the Docker registry. This is the same as the REG_PASSWORD value on prime.plane.so |
### License
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| planeVersion | vX.XX.X | Yes | Specifies the version of Plane to be deployed. Copy this from prime.plane.so. |
-| license.licenseServer | [https://prime.plane.so](https://prime.plane.so) | Yes | Sets the value of the `licenseServer` that gets you your license and validates it periodically. Don't change this. |
-| license.licenseKey | | Yes | Holds your license key to Plane One. Copy this from prime.plane.so. |
-| license.licenseDomain | 'plane.example.com' | Yes | The fully-qualified domain name (FQDN) in the format `sudomain.domain.tld` or `domain.tld` that the license is bound to. It is also attached to your `ingress` host to access Plane. |
+| Setting | Default | Required | Description |
+| --------------------- | :----------------------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| planeVersion | vX.XX.X | Yes | Specifies the version of Plane to be deployed. Copy this from prime.plane.so. |
+| license.licenseServer | [https://prime.plane.so](https://prime.plane.so) | Yes | Sets the value of the `licenseServer` that gets you your license and validates it periodically. Don't change this. |
+| license.licenseKey | | Yes | Holds your license key to Plane One. Copy this from prime.plane.so. |
+| license.licenseDomain | 'plane.example.com' | Yes | The fully-qualified domain name (FQDN) in the format `sudomain.domain.tld` or `domain.tld` that the license is bound to. It is also attached to your `ingress` host to access Plane. |
### Postgres
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| services.postgres.local_setup | true | | Plane uses `postgres` as the primary database to store all the transactional data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `postgres`. Mark it as `false` when using a remotely hosted database |
-| services.postgres.image | registry.plane.tools/plane/postgres:15.5-alpine | | Using this key, user must provide the docker image name to setup the stateful deployment of `postgres`. (must be set when `services.postgres.local_setup=true`)|
-| services.postgres.servicePort | 5432 | | This key sets the default port number to be used while setting up stateful deployment of `postgres`. |
-| services.postgres.cliConnectPort | | | If you intend to access the hosted stateful deployment of postgres using any of the client tools (e.g Postico), this key helps you expose the port. The mentioned port must not be occupied by any other applicaiton|
-| services.postgres.volumeSize | 2Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) |
-| env.pgdb_username | plane | | Database credentials are requried to access the hosted stateful deployment of `postgres`. Use this key to set the username for the stateful deployment. |
-| env.pgdb_password | plane | | Database credentials are requried to access the hosted stateful deployment of `postgres`. Use this key to set the password for the stateful deployment. |
-| env.pgdb_name | plane | | Database name to be used while setting up stateful deployment of `Postgres`|
-| env.pgdb_remote_url | | | Users can also decide to use the remote hosted database and link to Plane deployment. Ignoring all the above keys, set `services.postgres.local_setup` to `false` and set this key with remote connection url. |
+| Setting | Default | Required | Description |
+| -------------------------------- | :---------------------------------------------: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| services.postgres.local_setup | true | | Plane uses `postgres` as the primary database to store all the transactional data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `postgres`. Mark it as `false` when using a remotely hosted database |
+| services.postgres.image | registry.plane.tools/plane/postgres:15.5-alpine | | Using this key, user must provide the docker image name to setup the stateful deployment of `postgres`. (must be set when `services.postgres.local_setup=true`) |
+| services.postgres.servicePort | 5432 | | This key sets the default port number to be used while setting up stateful deployment of `postgres`. |
+| services.postgres.cliConnectPort | | | If you intend to access the hosted stateful deployment of postgres using any of the client tools (e.g Postico), this key helps you expose the port. The mentioned port must not be occupied by any other applicaiton |
+| services.postgres.volumeSize | 2Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) |
+| env.pgdb_username | plane | | Database credentials are requried to access the hosted stateful deployment of `postgres`. Use this key to set the username for the stateful deployment. |
+| env.pgdb_password | plane | | Database credentials are requried to access the hosted stateful deployment of `postgres`. Use this key to set the password for the stateful deployment. |
+| env.pgdb_name | plane | | Database name to be used while setting up stateful deployment of `Postgres` |
+| env.pgdb_remote_url | | | Users can also decide to use the remote hosted database and link to Plane deployment. Ignoring all the above keys, set `services.postgres.local_setup` to `false` and set this key with remote connection url. |
### Redis Setup
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| services.redis.local_setup | true | | Plane uses `redis` to cache the session authentication and other static data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `redis`. Mark it as `false` when using a remotely hosted database |
-| services.redis.image | registry.plane.tools/plane/redis:7.2.4-alpine | | Using this key, user must provide the docker image name to setup the stateful deployment of `redis`. (must be set when `services.redis.local_setup=true`)|
-| services.redis.servicePort | 6379 | | This key sets the default port number to be used while setting up stateful deployment of `redis`. |
-| services.redis.volumeSize | 500Mi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) |
-| env.remote_redis_url | | | Users can also decide to use the remote hosted database and link to Plane deployment. Ignoring all the above keys, set `services.redis.local_setup` to `false` and set this key with remote connection url. |
+| Setting | Default | Required | Description |
+| -------------------------- | :-------------------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| services.redis.local_setup | true | | Plane uses `redis` to cache the session authentication and other static data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `redis`. Mark it as `false` when using a remotely hosted database |
+| services.redis.image | registry.plane.tools/plane/redis:7.2.4-alpine | | Using this key, user must provide the docker image name to setup the stateful deployment of `redis`. (must be set when `services.redis.local_setup=true`) |
+| services.redis.servicePort | 6379 | | This key sets the default port number to be used while setting up stateful deployment of `redis`. |
+| services.redis.volumeSize | 500Mi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) |
+| env.remote_redis_url | | | Users can also decide to use the remote hosted database and link to Plane deployment. Ignoring all the above keys, set `services.redis.local_setup` to `false` and set this key with remote connection url. |
### Doc Store (Minio/S3) Setup
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| services.minio.local_setup | true | | Plane uses `minio` as the default file storage drive. This storage can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws S3 or similar services). Set this to `true` when you choose to setup stateful deployment of `minio`. Mark it as `false` when using a remotely hosted database |
-| services.minio.image | registry.plane.tools/plane/minio:latest | | Using this key, user must provide the docker image name to setup the stateful deployment of `minio`. (must be set when `services.minio.local_setup=true`)|
-| services.minio.volumeSize | 3Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) |
-| services.minio.root_user | admin | | Storage credentials are requried to access the hosted stateful deployment of `minio`. Use this key to set the username for the stateful deployment. |
-| services.minio.root_password | password | | Storage credentials are requried to access the hosted stateful deployment of `minio`. Use this key to set the password for the stateful deployment. |
-| env.docstore_bucket | uploads | Yes | Storage bucket name is required as part of configuration. This is where files will be uploaded irrespective of if you are using `Minio` or external `S3` (or compatible) storage service |
-| env.doc_upload_size_limit | 5242880 | Yes | Document Upload Size Limit (default to 5Mb) |
-| env.aws_access_key | | | External `S3` (or compatible) storage service provides `access key` for the application to connect and do the necessary upload/download operations. To be provided when `services.minio.local_setup=false` |
-| env.aws_secret_access_key | | | External `S3` (or compatible) storage service provides `secret access key` for the application to connect and do the necessary upload/download operations. To be provided when `services.minio.local_setup=false` |
-| env.aws_region | | | External `S3` (or compatible) storage service providers creates any buckets in user selected region. This is also shared with the user as `region` for the application to connect and do the necessary upload/download operations. To be provided when `services.minio.local_setup=false` |
-| env.aws_s3_endpoint_url | | | External `S3` (or compatible) storage service providers shares a `endpoint_url` for the integration purpose for the application to connect and do the necessary upload/download operations. To be provided when `services.minio.local_setup=false` |
+| Setting | Default | Required | Description |
+| ---------------------------- | :-------------------------------------: | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| services.minio.local_setup | true | | Plane uses `minio` as the default file storage drive. This storage can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws S3 or similar services). Set this to `true` when you choose to setup stateful deployment of `minio`. Mark it as `false` when using a remotely hosted database |
+| services.minio.image | registry.plane.tools/plane/minio:latest | | Using this key, user must provide the docker image name to setup the stateful deployment of `minio`. (must be set when `services.minio.local_setup=true`) |
+| services.minio.volumeSize | 3Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) |
+| services.minio.root_user | admin | | Storage credentials are requried to access the hosted stateful deployment of `minio`. Use this key to set the username for the stateful deployment. |
+| services.minio.root_password | password | | Storage credentials are requried to access the hosted stateful deployment of `minio`. Use this key to set the password for the stateful deployment. |
+| env.docstore_bucket | uploads | Yes | Storage bucket name is required as part of configuration. This is where files will be uploaded irrespective of if you are using `Minio` or external `S3` (or compatible) storage service |
+| env.doc_upload_size_limit | 5242880 | Yes | Document Upload Size Limit (default to 5Mb) |
+| env.aws_access_key | | | External `S3` (or compatible) storage service provides `access key` for the application to connect and do the necessary upload/download operations. To be provided when `services.minio.local_setup=false` |
+| env.aws_secret_access_key | | | External `S3` (or compatible) storage service provides `secret access key` for the application to connect and do the necessary upload/download operations. To be provided when `services.minio.local_setup=false` |
+| env.aws_region | | | External `S3` (or compatible) storage service providers creates any buckets in user selected region. This is also shared with the user as `region` for the application to connect and do the necessary upload/download operations. To be provided when `services.minio.local_setup=false` |
+| env.aws_s3_endpoint_url | | | External `S3` (or compatible) storage service providers shares a `endpoint_url` for the integration purpose for the application to connect and do the necessary upload/download operations. To be provided when `services.minio.local_setup=false` |
### Web Deployment
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| services.web.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
-| services.web.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use.|
-| services.web.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use.|
-| services.web.image| registry.plane.tools/plane/web-enterprise | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment |
+| Setting | Default | Required | Description |
+| ------------------------ | :---------------------------------------: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| services.web.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
+| services.web.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. |
+| services.web.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. |
+| services.web.image | registry.plane.tools/plane/web-enterprise | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment |
### Space Deployment
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| services.space.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
-| services.space.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use.|
-| services.space.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use.|
-| services.space.image| registry.plane.tools/plane/space-enterprise | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment |
+| Setting | Default | Required | Description |
+| -------------------------- | :-----------------------------------------: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| services.space.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
+| services.space.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. |
+| services.space.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. |
+| services.space.image | registry.plane.tools/plane/space-enterprise | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment |
### Admin Deployment
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| services.admin.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
-| services.admin.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use.|
-| services.admin.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use.|
-| services.admin.image| registry.plane.tools/plane/admin-enterprise | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment |
+| Setting | Default | Required | Description |
+| -------------------------- | :-----------------------------------------: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| services.admin.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
+| services.admin.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. |
+| services.admin.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. |
+| services.admin.image | registry.plane.tools/plane/admin-enterprise | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment |
### API Deployment
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| services.api.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
-| services.api.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use.|
-| services.api.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use.|
-| services.api.image| registry.plane.tools/plane/backend-enterprise | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment |
-| env.sentry_dsn | | | (optional) API service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry provided DSN for this integration.|
-| env.sentry_environment | | | (optional) API service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry environment name (as configured in Sentry) for this integration.|
+| Setting | Default | Required | Description |
+| ------------------------ | :-------------------------------------------: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| services.api.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
+| services.api.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. |
+| services.api.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. |
+| services.api.image | registry.plane.tools/plane/backend-enterprise | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment |
+| env.sentry_dsn | | | (optional) API service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry provided DSN for this integration. |
+| env.sentry_environment | | | (optional) API service deployment comes with some of the preconfigured integration. Sentry is one among those. Here user can set the Sentry environment name (as configured in Sentry) for this integration. |
### Worker Deployment
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| services.worker.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
-| services.worker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use.|
-| services.worker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use.|
+| Setting | Default | Required | Description |
+| --------------------------- | :-----: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| services.worker.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
+| services.worker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. |
+| services.worker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. |
### Beat-Worker deployment
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| services.beatworker.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
-| services.beatworker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use.|
-| services.beatworker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use.|
+| Setting | Default | Required | Description |
+| ------------------------------- | :-----: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| services.beatworker.replicas | 1 | Yes | Kubernetes helps you with scaling up/down the deployments. You can run 1 or more pods for each deployment. This key helps you setting up number of replicas you want to run for this deployment. It must be >=1 |
+| services.beatworker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use maximum memory they are allowed to use. This key sets the memory limit for this deployment to use. |
+| services.beatworker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use. |
### Ingress and SSL Setup
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| ingress.enabled | true | | Ingress setup in kubernetes is a common practice to expose application to the intended audience. Set it to `false` if you are using external ingress providers like `Cloudflare` |
-| ingress.minioHost | 'plane-services.minio.example.com' | | Based on above configuration, if you want to expose the `minio` web console to set of users, use this key to set the `host` mapping or leave it as `EMPTY` to not expose interface. |
-| ingress.ingressClass | 'nginx' | Yes | Kubernetes cluster setup comes with various options of `ingressClass`. Based on your setup, set this value to the right one (eg. nginx, traefik, etc). Leave it to default in case you are using external ingress provider.|
-| ingress.ingress_annotations | `{ "nginx.ingress.kubernetes.io/proxy-body-size": "5m" }` | | Ingress controllers comes with various configuration options which can be passed as annotations. Setting this value lets you change the default value to user required. |
-| ssl.createIssuer | false | | Kubernets cluster setup supports creating `issuer` type resource. After deployment, this is step towards creating secure access to the ingress url. Issuer is required for you generate SSL certifiate. Kubernetes can be configured to use any of the certificate authority to generate SSL (depending on CertManager configuration). Set it to `true` to create the issuer. Applicable only when `ingress.enabled=true`|
-| ssl.issuer | http | | CertManager configuration allows user to create issuers using `http` or any of the other DNS Providers like `cloudflare`, `digitalocean`, etc. As of now Plane supports `http`, `cloudflare`, `digitalocean`|
-| ssl.token | | | To create issuers using DNS challenge, set the issuer api token of dns provider like cloudflare` or `digitalocean`(not required for http) |
-| ssl.server | [https://acme-v02.api.letsencrypt.org/directory](https://acme-v02.api.letsencrypt.org/directory) | | Issuer creation configuration need the certificate generation authority server url. Default URL is the `Let's Encrypt` server|
-| ssl.email | `plane@example.com` | | Certificate generation authority needs a valid email id before generating certificate. Required when `ssl.createIssuer=true` |
-| ssl.generateCerts | false | | After creating the issuers, user can still not create the certificate untill sure of configuration. Setting this to `true` will try to generate SSL certificate and associate with ingress. Applicable only when `ingress.enabled=true` and `ssl.createIssuer=true` |
+| Setting | Default | Required | Description |
+| --------------------------- | :----------------------------------------------------------------------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| ingress.enabled | true | | Ingress setup in kubernetes is a common practice to expose application to the intended audience. Set it to `false` if you are using external ingress providers like `Cloudflare` |
+| ingress.minioHost | 'plane-services.minio.example.com' | | Based on above configuration, if you want to expose the `minio` web console to set of users, use this key to set the `host` mapping or leave it as `EMPTY` to not expose interface. |
+| ingress.ingressClass | 'nginx' | Yes | Kubernetes cluster setup comes with various options of `ingressClass`. Based on your setup, set this value to the right one (eg. nginx, traefik, etc). Leave it to default in case you are using external ingress provider. |
+| ingress.ingress_annotations | `{ "nginx.ingress.kubernetes.io/proxy-body-size": "5m" }` | | Ingress controllers comes with various configuration options which can be passed as annotations. Setting this value lets you change the default value to user required. |
+| ssl.createIssuer | false | | Kubernets cluster setup supports creating `issuer` type resource. After deployment, this is step towards creating secure access to the ingress url. Issuer is required for you generate SSL certifiate. Kubernetes can be configured to use any of the certificate authority to generate SSL (depending on CertManager configuration). Set it to `true` to create the issuer. Applicable only when `ingress.enabled=true` |
+| ssl.issuer | http | | CertManager configuration allows user to create issuers using `http` or any of the other DNS Providers like `cloudflare`, `digitalocean`, etc. As of now Plane supports `http`, `cloudflare`, `digitalocean` |
+| ssl.token | | | To create issuers using DNS challenge, set the issuer api token of dns provider like cloudflare`or`digitalocean`(not required for http) |
+| ssl.server | [https://acme-v02.api.letsencrypt.org/directory](https://acme-v02.api.letsencrypt.org/directory) | | Issuer creation configuration need the certificate generation authority server url. Default URL is the `Let's Encrypt` server |
+| ssl.email | `plane@example.com` | | Certificate generation authority needs a valid email id before generating certificate. Required when `ssl.createIssuer=true` |
+| ssl.generateCerts | false | | After creating the issuers, user can still not create the certificate untill sure of configuration. Setting this to `true` will try to generate SSL certificate and associate with ingress. Applicable only when `ingress.enabled=true` and `ssl.createIssuer=true` |
### Common Environment Settings
-| Setting | Default | Required | Description |
-|---|:---:|:---:|---|
-| env.storageClass | longhorn | | Creating the persitant volumes for the stateful deployments needs the `storageClass` name. Set the correct value as per your kubernetes cluster configuration. |
-| env.secret_key | 60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 | Yes | This must a random string which is used for hashing/encrypting the sensitive data within the application. Once set, changing this might impact the already hashed/encrypted data|
+| Setting | Default | Required | Description |
+| ---------------- | :------------------------------------------------: | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| env.storageClass | longhorn | | Creating the persitant volumes for the stateful deployments needs the `storageClass` name. Set the correct value as per your kubernetes cluster configuration. |
+| env.secret_key | 60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 | Yes | This must a random string which is used for hashing/encrypting the sensitive data within the application. Once set, changing this might impact the already hashed/encrypted data |
## Custom Ingress Routes
If you are planning to use 3rd party ingress providers, here is the available route configuration
-| Host | Path | Service |
-|--- |:---:|---|
-| plane.example.com | / | [http://plane-web.plane-one:3000](http://plane-web.plane-one:3000) |
-| plane.example.com | /spaces/* | [http://plane-space.plane-one:3000](http://plane-space.plane-one:3000) |
-| plane.example.com | /god-mode/* | [http://plane-admin.plane-one:8000](http://plane-admin.plane-one:8000) |
-| plane.example.com | /api/* | [http://plane-api.plane-one:8000](http://plane-api.plane-one:8000) |
-| plane.example.com | /auth/* | [http://plane-api.plane-one:8000](http://plane-api.plane-one:8000)|
-| plane.example.com | /uploads/* | [http://plane-minio.plane-one:9000](http://plane-minio.plane-one:9000) |
-| plane-minio.example.com | / | [http://plane-minio.plane-one:9000](http://plane-minio.plane-one:9000) |
\ No newline at end of file
+| Host | Path | Service |
+| ----------------------- | :----------: | ---------------------------------------------------------------------- |
+| plane.example.com | / | [http://plane-web.plane-one:3000](http://plane-web.plane-one:3000) |
+| plane.example.com | /spaces/\* | [http://plane-space.plane-one:3000](http://plane-space.plane-one:3000) |
+| plane.example.com | /god-mode/\* | [http://plane-admin.plane-one:8000](http://plane-admin.plane-one:8000) |
+| plane.example.com | /api/\* | [http://plane-api.plane-one:8000](http://plane-api.plane-one:8000) |
+| plane.example.com | /auth/\* | [http://plane-api.plane-one:8000](http://plane-api.plane-one:8000) |
+| plane.example.com | /uploads/\* | [http://plane-minio.plane-one:9000](http://plane-minio.plane-one:9000) |
+| plane-minio.example.com | / | [http://plane-minio.plane-one:9000](http://plane-minio.plane-one:9000) |
diff --git a/docs/plane-one/self-host/overview.md b/docs/plane-one/self-host/overview.md
index a4853203..86cb9dae 100644
--- a/docs/plane-one/self-host/overview.md
+++ b/docs/plane-one/self-host/overview.md
@@ -4,21 +4,24 @@ description: Self-hosting overview. Complete guide and documentation for Plane.
keywords: plane
---
-
# Self-hosting overview
## Methods
### [Docker](/self-hosting/methods/docker-compose)
+
Run Plane One using docker with a single command
### Kubernetes (Coming Soon!)
+
Run Plane One on a kubernetes cluster using helm package manager.
## Manage
### [Manage Licenses](https://docs.plane.so/workspaces-and-users/upgrade-plan)
+
Learn how to configure your instance.
### [Prime CLI](/self-hosting/manage/prime-cli)
+
Setup authentication methods on your Plane instance.
diff --git a/docs/self-hosting/editions-and-versions.md b/docs/self-hosting/editions-and-versions.md
index 03fa1e0a..c688fae0 100644
--- a/docs/self-hosting/editions-and-versions.md
+++ b/docs/self-hosting/editions-and-versions.md
@@ -4,7 +4,6 @@ description: Learn how to understanding plane's editions for self-hosted Plane.
keywords: plane, self-hosting, deployment, plane installation
---
-
# Understanding Plane's editions
Plane comes in four editions by how its deployed. Our Cloud is our only hosted edition as of 2025. Additionally, we offer three unique self-hosted editions tailored to meet two sets of unique needs—the open-source Community Edition, the recommended Commercial Edition, and the Airgapped Edition.
@@ -43,21 +42,20 @@ Built for organizations with strict security and compliance requirements, the Ai
The Airgapped Edition offers:
-- **Complete isolation**
-Operates entirely within your network perimeter with no external dependencies or outbound connections.
-
-- **Full feature parity**
-Includes all features available in the standard Commercial Edition, including advanced work management, security controls, and governance tools
+- **Complete isolation**
+ Operates entirely within your network perimeter with no external dependencies or outbound connections.
-- **Version updates**
- Updates from your own docker registry.
+- **Full feature parity**
+ Includes all features available in the standard Commercial Edition, including advanced work management, security controls, and governance tools
-- **Self-contained architecture**
-All services, dependencies, and resources are bundled for deployment in restricted networks
+- **Version updates**
+ Updates from your own docker registry.
-- **Compliance-ready**
-Designed to meet requirements for environments that prohibit external network communication
+- **Self-contained architecture**
+ All services, dependencies, and resources are bundled for deployment in restricted networks
+- **Compliance-ready**
+ Designed to meet requirements for environments that prohibit external network communication
## Why we separate editions
@@ -83,7 +81,6 @@ Each of our editions is built on a distinct codebase. Versions with each differ
For both the Commercial, Airgapped, and Community Editions, version updates are in your control. Regular updates ensure you’re benefiting from the latest features and improvements. See [Update Plane](/self-hosting/manage/upgrade-plane) for how to upgrade your versions.
-
## Changelog
-We maintain a detailed changelog for all editions. [Check it out](https://plane.so/changelog) and bookmark it to stay informed about the latest features, bug fixes, and improvements by edition.
\ No newline at end of file
+We maintain a detailed changelog for all editions. [Check it out](https://plane.so/changelog) and bookmark it to stay informed about the latest features, bug fixes, and improvements by edition.
diff --git a/docs/self-hosting/govern/advanced-search.md b/docs/self-hosting/govern/advanced-search.md
index 4ba7f515..ae70aa2e 100644
--- a/docs/self-hosting/govern/advanced-search.md
+++ b/docs/self-hosting/govern/advanced-search.md
@@ -4,15 +4,14 @@ description: Learn how to configure opensearch for advanced search for self-host
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure OpenSearch for advanced search
-
Plane uses OpenSearch to provide advanced search capabilities across your workspace. This guide walks you through setting up OpenSearch integration on your self-hosted instance.
## Before you begin
You'll need:
+
- An OpenSearch 2.x instance (self-hosted or managed service like AWS OpenSearch).
## What you get with advanced search
@@ -28,54 +27,59 @@ Users can access advanced search using the global search shortcut (Cmd/Ctrl + K)
## Configure OpenSearch
-Set environment variables in your Plane configuration. See [Environment variables reference](/self-hosting/govern/environment-variables#opensearch) for details.
+Set environment variables in your Plane configuration. See [Environment variables reference](/self-hosting/govern/environment-variables#opensearch) for details.
### For Docker deployments
1. **Add configuration to your environment file**
Edit `/opt/plane/plane.env`.
- ```bash
- # OpenSearch Settings
- OPENSEARCH_ENABLED=1
- OPENSEARCH_URL=https://your-opensearch-instance:9200/
- OPENSEARCH_USERNAME=admin
- OPENSEARCH_PASSWORD=your-secure-password
- OPENSEARCH_INDEX_PREFIX=plane
- ```
+
+ ```bash
+ # OpenSearch Settings
+ OPENSEARCH_ENABLED=1
+ OPENSEARCH_URL=https://your-opensearch-instance:9200/
+ OPENSEARCH_USERNAME=admin
+ OPENSEARCH_PASSWORD=your-secure-password
+ OPENSEARCH_INDEX_PREFIX=plane
+ ```
2. **Restart Plane services**
- ```bash
- prime-cli restart
- ```
+
+ ```bash
+ prime-cli restart
+ ```
or if managing containers directly:
- ```bash
- docker compose down
- docker compose up -d
- ```
+
+ ```bash
+ docker compose down
+ docker compose up -d
+ ```
3. **Create search indices**
Access the API container and create the necessary indices:
- ```bash
- # Access the API container
- docker exec -it plane-api-1 sh
- # Create all search indices (run once)
- python manage.py manage_search_index index rebuild --force
- ```
+ ```bash
+ # Access the API container
+ docker exec -it plane-api-1 sh
+
+ # Create all search indices (run once)
+ python manage.py manage_search_index index rebuild --force
+ ```
4. **Index your existing data**
Index all existing content into OpenSearch:
- ```bash
- # For small datasets
- python manage.py manage_search_index document index --force
- # For large datasets (recommended)
- python manage.py manage_search_index --background document index --force
- ```
+ ```bash
+ # For small datasets
+ python manage.py manage_search_index document index --force
+
+ # For large datasets (recommended)
+ python manage.py manage_search_index --background document index --force
+ ```
The background option processes indexing through Celery workers, which is better for instances with large amounts of data.
@@ -86,59 +90,65 @@ The Plane Helm chart provides auto-setup for OpenSearch. If you're using your ow
1. **Configure Helm values**
Get the current values file:
- ```bash
- helm show values plane/plane-enterprise > values.yaml
- ```
+
+ ```bash
+ helm show values plane/plane-enterprise > values.yaml
+ ```
Edit `values.yaml` to add OpenSearch configuration:
- ```yaml
- env:
- # OpenSearch configuration
- opensearch_remote_url: 'https://your-opensearch-instance:9200/'
- opensearch_remote_username: 'admin'
- opensearch_remote_password: 'your-secure-password'
- opensearch_index_prefix: 'plane'
- ```
+
+ ```yaml
+ env:
+ # OpenSearch configuration
+ opensearch_remote_url: 'https://your-opensearch-instance:9200/'
+ opensearch_remote_username: 'admin'
+ opensearch_remote_password: 'your-secure-password'
+ opensearch_index_prefix: 'plane'
+ ```
Refer to the [Plane Helm chart documentation](https://artifacthub.io/packages/helm/makeplane/plane-enterprise?modal=values&path=env.opensearch_remote_url) for complete values structure.
2. **Upgrade your deployment**
- ```bash
- helm upgrade --install plane-app plane/plane-enterprise \
- --create-namespace \
- --namespace plane \
- -f values.yaml \
- --timeout 10m \
- --wait \
- --wait-for-jobs
- ```
+
+ ```bash
+ helm upgrade --install plane-app plane/plane-enterprise \
+ --create-namespace \
+ --namespace plane \
+ -f values.yaml \
+ --timeout 10m \
+ --wait \
+ --wait-for-jobs
+ ```
3. **Create search indices**
Run these commands in the API pod.
- ```bash
- # Get the API pod name
- API_POD=$(kubectl get pods -n plane --no-headers | grep api | head -1 | awk '{print $1}')
- # Create all search indices (run once)
- kubectl exec -n plane $API_POD -- python manage.py manage_search_index index rebuild --force
- ```
+ ```bash
+ # Get the API pod name
+ API_POD=$(kubectl get pods -n plane --no-headers | grep api | head -1 | awk '{print $1}')
+
+ # Create all search indices (run once)
+ kubectl exec -n plane $API_POD -- python manage.py manage_search_index index rebuild --force
+ ```
4. **Index your existing data**
- Run these commands in the API pod.
- ```bash
- # For small datasets
- kubectl exec -n plane $API_POD -- python manage.py manage_search_index document index --force
+ Run these commands in the API pod.
+
+ ```bash
+ # For small datasets
+ kubectl exec -n plane $API_POD -- python manage.py manage_search_index document index --force
- # For large datasets (recommended)
- kubectl exec -n plane $API_POD -- python manage.py manage_search_index --background document index --force
- ```
+ # For large datasets (recommended)
+ kubectl exec -n plane $API_POD -- python manage.py manage_search_index --background document index --force
+ ```
## Verify the setup
### Check OpenSearch connection
Test that Plane can connect to your OpenSearch instance:
+
```bash
# Access your API container or pod
docker exec -it plane-api-1 sh # For Docker
@@ -150,6 +160,7 @@ python manage.py shell
```
Then run:
+
```python
from django.conf import settings
from opensearchpy import OpenSearch
@@ -171,6 +182,7 @@ print(client.cat.indices(format='json'))
### Verify indices were created
List all created indices:
+
```bash
python manage.py manage_search_index list
```
@@ -189,6 +201,7 @@ You should see indices for work items, projects, cycles, modules, pages, and oth
### Resync data
If search results become stale or inconsistent, resync your data:
+
```bash
python manage.py manage_search_index document index --force
```
@@ -198,6 +211,7 @@ This reindexes all content without recreating the index structure.
### Complete rebuild
For a complete reset (recreates indices and reindexes all data):
+
```bash
# Recreate all indices
python manage.py manage_search_index index rebuild --force
@@ -213,11 +227,13 @@ Use this if index structure needs updating or if you're experiencing persistent
Check API logs OpenSearch-related errors:
**Docker:**
+
```bash
docker compose logs api | grep -i opensearch
```
**Kubernetes:**
+
```bash
kubectl logs -n plane -l app.kubernetes.io/component=api | grep -i opensearch
```
@@ -247,6 +263,7 @@ Instead, Plane batches updates through Redis. When a signal fires, the update go
The batching pattern also provides resilience. If OpenSearch is temporarily unavailable, updates accumulate in Redis and process once connectivity returns. This requires Redis 6.2+ which supports the LPOP count operation needed for efficient batch retrieval.
### The complete flow
+

When you search, queries bypass this synchronization process entirely. The Plane API sends your search query directly to OpenSearch, which returns results almost instantly. Your database isn't involved in search queries at all — this is the key to search performance.
@@ -259,16 +276,16 @@ The answer lies in how different entities need different search behaviors. Work
Each index is optimized for its content type:
-| Index | Content | Search Features |
-|-------|---------|-----------------|
-| `{prefix}_issues` | Work items | Full-text search, field weighting (title > description), state filtering |
-| `{prefix}_issue_comments` | Comments | Comment search within work items, parent-child relationships |
-| `{prefix}_projects` | Projects | Project discovery, metadata filtering (dates, counts, status) |
-| `{prefix}_cycles` | Cycles | Cycle search, time-based filtering and aggregations |
-| `{prefix}_modules` | Modules | Module/sprint search, planning aggregations |
-| `{prefix}_pages` | Pages | Page content search, rich text analysis for long-form content |
-| `{prefix}_workspaces` | Workspaces | Workspace search and discovery |
-| `{prefix}_issue_views` | Saved views | Saved view search and filtering |
-| `{prefix}_teamspaces` | Teamspaces | Teamspace discovery |
-
-The `{prefix}` is whatever you configured in `OPENSEARCH_INDEX_PREFIX`, or empty if you didn't set a prefix. This prefix exists because you might run multiple Plane instances pointing to the same OpenSearch cluster. The prefix prevents different instances from accidentally sharing or conflicting with each other's indices.
\ No newline at end of file
+| Index | Content | Search Features |
+| ------------------------- | ----------- | ------------------------------------------------------------------------ |
+| `{prefix}_issues` | Work items | Full-text search, field weighting (title > description), state filtering |
+| `{prefix}_issue_comments` | Comments | Comment search within work items, parent-child relationships |
+| `{prefix}_projects` | Projects | Project discovery, metadata filtering (dates, counts, status) |
+| `{prefix}_cycles` | Cycles | Cycle search, time-based filtering and aggregations |
+| `{prefix}_modules` | Modules | Module/sprint search, planning aggregations |
+| `{prefix}_pages` | Pages | Page content search, rich text analysis for long-form content |
+| `{prefix}_workspaces` | Workspaces | Workspace search and discovery |
+| `{prefix}_issue_views` | Saved views | Saved view search and filtering |
+| `{prefix}_teamspaces` | Teamspaces | Teamspace discovery |
+
+The `{prefix}` is whatever you configured in `OPENSEARCH_INDEX_PREFIX`, or empty if you didn't set a prefix. This prefix exists because you might run multiple Plane instances pointing to the same OpenSearch cluster. The prefix prevents different instances from accidentally sharing or conflicting with each other's indices.
diff --git a/docs/self-hosting/govern/authentication.md b/docs/self-hosting/govern/authentication.md
index f56fea47..32884f7e 100644
--- a/docs/self-hosting/govern/authentication.md
+++ b/docs/self-hosting/govern/authentication.md
@@ -4,7 +4,6 @@ description: Configure authentication methods for self-hosted Plane. Setup OAuth
keywords: plane, self-hosting, deployment, plane installation, authentication, sso, oauth, configuration, administration
---
-
# Overview
Plane offers several methods you can choose from to let your users log in to your Plane instance. Configure these methods in Authentication on /god-mode of your instance.
@@ -21,4 +20,4 @@ Plane lets your users log in with codes sent over email. This is disabled if SMT
### Passwords
-Your users can log in with passwords that they or you set for them. This is toggled on when SMTP isn't configured for your instance. Disable it if you would like to use another authentication method below.
\ No newline at end of file
+Your users can log in with passwords that they or you set for them. This is toggled on when SMTP isn't configured for your instance. Disable it if you would like to use another authentication method below.
diff --git a/docs/self-hosting/govern/communication.md b/docs/self-hosting/govern/communication.md
index ac9c812c..920b192f 100644
--- a/docs/self-hosting/govern/communication.md
+++ b/docs/self-hosting/govern/communication.md
@@ -4,7 +4,6 @@ description: Configure SMTP email settings for Plane. Setup email notifications
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure SMTP for email notifications
Either during your set-up or sometime later, you will want to set SMTP settings to let your users get emails to reset passwords, onboard themselves right, and get notifications for changes, and receive exports of your data.
@@ -35,7 +34,6 @@ Navigate to `Email` in `/god-mode`and you will see ↓.
- **Password**\
Specify the password for the SMTP configuration here.
-
::: tip
**Google Workspaces**
@@ -44,7 +42,6 @@ If your Plane instance is not accessible on the internet, Gmail may block profil
To resolve this issue, you must configure the Image URL proxy allowlist in your Google Workspace settings to include your Plane instance's URL. Refer to Google’s documentation for instructions: [Allowlist image URLs](https://support.google.com/a/answer/3299041?hl=en).
:::
-
## Configuration for popular email services providers
### Amazon SES
@@ -56,4 +53,4 @@ To resolve this issue, you must configure the Image URL proxy allowlist in your
5. Select **Show User SMTP Credentials** to view the user's SMTP credentials.
6. Return to your Plane instance's `/god-mode` and enter the obtained details.
-Ensure to review [**email quotas**](https://docs.aws.amazon.com/ses/latest/dg/quotas.html) for your Amazon SES server. Consider managing email recipients using groups to optimize usage.
\ No newline at end of file
+Ensure to review [**email quotas**](https://docs.aws.amazon.com/ses/latest/dg/quotas.html) for your Amazon SES server. Consider managing email recipients using groups to optimize usage.
diff --git a/docs/self-hosting/govern/configure-dns-email-service.md b/docs/self-hosting/govern/configure-dns-email-service.md
index c599eae1..81d35444 100644
--- a/docs/self-hosting/govern/configure-dns-email-service.md
+++ b/docs/self-hosting/govern/configure-dns-email-service.md
@@ -4,10 +4,9 @@ description: Learn how to configure dns for intake email for self-hosted Plane.
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure DNS for Intake Email
-This guide explains how to configure DNS settings to enable the [Intake Email](https://docs.plane.so/intake/intake-email) feature for your self-hosted Plane instance. These configurations enable your server to accept messages sent to your project's dedicated Intake address, which are then converted into work items in your project's Intake section.
+This guide explains how to configure DNS settings to enable the [Intake Email](https://docs.plane.so/intake/intake-email) feature for your self-hosted Plane instance. These configurations enable your server to accept messages sent to your project's dedicated Intake address, which are then converted into work items in your project's Intake section.
## Prerequisites
@@ -19,54 +18,63 @@ If any of these ports are currently in use, you can free them by running:
fuser -k 25/tcp 465/tcp 587/tcp
```
-## Generate SSL/TLS Certificate for Email Domain
+## Generate SSL/TLS Certificate for Email Domain
+
::: warning
Mandatory for Docker Compose deployments only.
:::
Before configuring DNS records for Intake Email, secure your email domain with an SSL/TLS certificate. This ensures encrypted communication between mail servers and improves email trust and deliverability.
-1. **Install Certbot**
-Update your system and install Certbot.
+1. **Install Certbot**
+ Update your system and install Certbot.
+
```bash
sudo apt update && sudo apt install certbot
```
+
For NGINX:
+
```bash
sudo apt install python3-certbot-nginx
```
+
For Apache:
+
```bash
sudo apt install python3-certbot-apache
```
-2. **Generate SSL Certificate**
-Choose the method that matches your web server setup:
+2. **Generate SSL Certificate**
+ Choose the method that matches your web server setup:
For NGINX:
+
```bash
sudo certbot --nginx -d
```
For Apache:
+
```bash
sudo certbot --apache -d
```
For standalone (no web server):
+
```bash
sudo certbot certonly --standalone -d
```
-3. **Copy Certificate Files**
-Copy the generated certificate files to Plane's expected directory:
+3. **Copy Certificate Files**
+ Copy the generated certificate files to Plane's expected directory:
```bash
- sudo cp /etc/letsencrypt/live//fullchain.pem /opt/plane/data/email/tls/cert.pem
+ sudo cp /etc/letsencrypt/live//fullchain.pem /opt/plane/data/email/tls/cert.pem
sudo cp /etc/letsencrypt/live//privkey.pem /opt/plane/data/email/tls/key.pem
```
-4. **Configure Environment Variables**
-Add the following settings to your plane.env file:
+4. **Configure Environment Variables**
+ Add the following settings to your plane.env file:
```bash
# If using SMTP_DOMAIN as FQDN (e.g., intake.example.com),
@@ -81,25 +89,25 @@ Add the following settings to your plane.env file:
Important: `SMTP_DOMAIN` and `INTAKE_EMAIL_DOMAIN` must be identical.
:::
-
## Configure DNS records
-1. **Create an A Record**
-This record points to the server running your email service.
+1. **Create an A Record**
+ This record points to the server running your email service.
```bash
Type: A
Host: # Example: plane.example.com
Value: # Your server's public IP address
TTL: Auto | 3600
- ```
+ ```
- ::: tip
+ ::: tip
You can alternatively use a CNAME record if you're using a cloud load balancer.
:::
-2. **Add an MX Record**
-This record directs email traffic to your mail server.
+2. **Add an MX Record**
+ This record directs email traffic to your mail server.
+
```bash
Type: MX
Host: # Example: intake.example.com
@@ -108,17 +116,18 @@ This record directs email traffic to your mail server.
TTL: Auto | 3600
```
-3. **Configure an SPF Record**
-This record helps prevent email spoofing.
+3. **Configure an SPF Record**
+ This record helps prevent email spoofing.
```bash
Type: TXT
Host: # Example: intake.example.com
Value: "v=spf1 ip4: -all"
TTL: Auto | 3600
- ```
-4. **Set Up a DMARC record**
-This record specifies how receiving mail servers should handle authentication failures.
+ ```
+
+4. **Set Up a DMARC record**
+ This record specifies how receiving mail servers should handle authentication failures.
```bash
Type: TXT
@@ -126,7 +135,9 @@ This record specifies how receiving mail servers should handle authentication fa
Value: "v=DMARC1; p=reject; rua=mailto:"
TTL: Auto | 3600
```
+
## Verify your configuration
+
After setting up your DNS records, verify that they're correctly configured:
```bash
@@ -146,6 +157,7 @@ dig TXT _dmarc.
You can also use [MXToolbox](https://mxtoolbox.com) to check for any issues with your DNS configuration.
## Test your mail server
+
Once your DNS records have propagated, test your SMTP connections:
```bash
@@ -158,16 +170,14 @@ telnet 587
## Troubleshooting
- MX Record issues
-
- - Ensure there's a proper dot at the end of the domain.
- - Check that the priority number is correct (lower = higher priority).
- - Allow 24-48 hours for DNS changes to fully propagate.
+ - Ensure there's a proper dot at the end of the domain.
+ - Check that the priority number is correct (lower = higher priority).
+ - Allow 24-48 hours for DNS changes to fully propagate.
- A Record issues
-
- - Verify that the IP address is correct.
- - Ensure your mail subdomain matches the MX record.
+ - Verify that the IP address is correct.
+ - Ensure your mail subdomain matches the MX record.
## See also
-[Intake Email](https://docs.plane.so/intake/intake-email)
\ No newline at end of file
+[Intake Email](https://docs.plane.so/intake/intake-email)
diff --git a/docs/self-hosting/govern/configure-ssl.md b/docs/self-hosting/govern/configure-ssl.md
index 30720b89..7bdc6431 100644
--- a/docs/self-hosting/govern/configure-ssl.md
+++ b/docs/self-hosting/govern/configure-ssl.md
@@ -4,7 +4,6 @@ description: Configure SSL/TLS certificates for Plane. Setup HTTPS encryption fo
keywords: plane, self-hosting, deployment, plane installation, configuration, administration, ssl, https, security
---
-
# Set up SSL
This guide shows you how to configure SSL/TLS certificates for your self-hosted Plane instance. Plane handles certificate provisioning and renewal automatically using Let's Encrypt.
@@ -18,6 +17,7 @@ If you're using an external reverse proxy (nginx, Caddy, Traefik) or a load bala
## Before you begin
Ensure you have:
+
- A registered domain name pointing to your Plane server
- DNS records configured (A or CNAME record pointing to your server's IP)
- Ports 80 and 443 open on your server's firewall
@@ -32,6 +32,7 @@ Ensure you have:
### Open the configuration file
Edit your Plane environment configuration:
+
```bash
vim /opt/plane/plane.env
```
@@ -39,6 +40,7 @@ vim /opt/plane/plane.env
### Set required variables
Add or update these environment variables:
+
```bash
# SSL Configuration
CERT_EMAIL=admin@yourcompany.com
@@ -60,11 +62,13 @@ Your full Plane URL **with** the `https://` protocol. This tells Plane services
### DNS provider configuration (optional)
If you're using Cloudflare or another DNS provider with API access, you can use DNS validation instead of HTTP validation. This is useful if:
+
- Your server is behind a firewall that blocks port 80
- You need wildcard certificates
- HTTP validation isn't working due to network restrictions
**For Cloudflare:**
+
```bash
CERT_ACME_DNS=acme_dns cloudflare
```
@@ -78,11 +82,13 @@ Check the [acme.sh DNS API documentation](https://github.com/acmesh-official/acm
## Apply SSL configuration
Restart Plane to apply the SSL settings:
+
```bash
sudo prime-cli restart
```
Prime CLI will:
+
1. Stop all Plane services
2. Request a new SSL certificate from Let's Encrypt
3. Configure the built-in proxy to use HTTPS
@@ -93,6 +99,7 @@ This process typically takes 30-60 seconds.
## Verify SSL is working
Check that your Plane instance is accessible via HTTPS:
+
```bash
curl -I https://plane.yourcompany.com
```
@@ -101,7 +108,6 @@ You should see a response with `HTTP/2 200` or `HTTP/1.1 200` and SSL-related he
Visit your Plane instance in a browser at `https://plane.yourcompany.com`. You should see a secure connection (padlock icon) without certificate warnings.
-
## Using custom SSL certificates
-Custom SSL certificates (from a corporate CA or purchased certificates) are not currently supported in Plane's deployment.
\ No newline at end of file
+Custom SSL certificates (from a corporate CA or purchased certificates) are not currently supported in Plane's deployment.
diff --git a/docs/self-hosting/govern/custom-domain.md b/docs/self-hosting/govern/custom-domain.md
index 274291d2..3f2b9e2a 100644
--- a/docs/self-hosting/govern/custom-domain.md
+++ b/docs/self-hosting/govern/custom-domain.md
@@ -4,7 +4,6 @@ description: Configure custom domain for self-hosted Plane. Setup your own domai
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure custom domain
During installation, you configure a domain for your instance. If you need to change that domain later, whether you're moving to a production domain, switching to a different hostname, or updating your DNS configuration, this guide walks you through the process.
@@ -29,7 +28,8 @@ cat /opt/plane/plane.env | grep
```
**Example output:**
-```env
+
+```ini
DOMAIN_NAME=localhost
SITE_ADDRESS=http://localhost
WEB_URL=http://localhost
@@ -41,54 +41,56 @@ This shows you all the variables that contain your current domain. You'll update
## Update domain in environment file
1. Open the Plane environment configuration file:
- ```bash
- vim /opt/plane/plane.env
- ```
+
+ ```bash
+ vim /opt/plane/plane.env
+ ```
2. Find and update these environment variables with your new domain:
+ - **DOMAIN_NAME**
- - **DOMAIN_NAME**
+ Set this to your bare domain name without protocol:
- Set this to your bare domain name without protocol:
- ```env
- DOMAIN_NAME=plane.company.com
- ```
+ ```ini
+ DOMAIN_NAME=plane.company.com
+ ```
- Don't include `http://` or `https://` here, just the hostname.
+ Don't include `http://` or `https://` here, just the hostname.
+ - **SITE_ADDRESS**
- - **SITE_ADDRESS**
+ Set this to your full domain URL:
- Set this to your full domain URL:
- ```env
- SITE_ADDRESS=https://plane.company.com
- ```
+ ```ini
+ SITE_ADDRESS=https://plane.company.com
+ ```
- Include the protocol (`https://` for SSL, `http://` if you haven't set up SSL yet).
+ Include the protocol (`https://` for SSL, `http://` if you haven't set up SSL yet).
+ - **WEB_URL**
- - **WEB_URL**
+ This should match your SITE_ADDRESS:
- This should match your SITE_ADDRESS:
- ```env
- WEB_URL=https://plane.company.com
- ```
+ ```ini
+ WEB_URL=https://plane.company.com
+ ```
- Again, include the full protocol.
+ Again, include the full protocol.
- **CORS_ALLOWED_ORIGINS**
+ **CORS_ALLOWED_ORIGINS**
- List all domains that should be allowed to make cross-origin requests to your Plane instance. This typically includes both HTTP and HTTPS versions of your domain:
- ```env
- CORS_ALLOWED_ORIGINS=https://plane.company.com,http://plane.company.com
- ```
+ List all domains that should be allowed to make cross-origin requests to your Plane instance. This typically includes both HTTP and HTTPS versions of your domain:
- Separate multiple entries with commas, no spaces. If you have multiple domains or subdomains that need access, add them all here.
+ ```ini
+ CORS_ALLOWED_ORIGINS=https://plane.company.com,http://plane.company.com
+ ```
+
+ Separate multiple entries with commas, no spaces. If you have multiple domains or subdomains that need access, add them all here.
## Restart Plane services
Apply your configuration changes by restarting Plane:
- ```bash
+`bash
sudo prime-cli restart
- ```
+ `
This process typically takes a few minutes. You'll see output indicating the status of each service as it restarts.
@@ -138,4 +140,4 @@ If your server is behind a firewall or router and has an internal IP address, yo
By following these steps, you will be able to access your self-hosted instance of Plane using your custom domain name, whether your server has a public IP address or is behind a firewall with an internal IP address.
-:::
\ No newline at end of file
+:::
diff --git a/docs/self-hosting/govern/database-and-storage.md b/docs/self-hosting/govern/database-and-storage.md
index f7e196d4..e7060b25 100644
--- a/docs/self-hosting/govern/database-and-storage.md
+++ b/docs/self-hosting/govern/database-and-storage.md
@@ -4,7 +4,6 @@ description: Configure external database and storage for Plane. Setup PostgreSQL
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure externalservices
The Prime CLI lets you easily configure your Commercial Edition instance, providing options to customize the PostgreSQL database, Redis, external storage, and other advanced settings.
@@ -13,78 +12,76 @@ The Prime CLI lets you easily configure your Commercial Edition instance, provid
**Prime CLI is for Docker installations only.** These commands only work on Plane instances originally installed using `prime-cli`.
:::
-1. Run the Prime CLI with ↓:
+1. Run the Prime CLI with ↓:
- ```sudo prime-cli```
+ `sudo prime-cli`
-2. Once the CLI is running, enter `configure`, which will guide you through a step-by-step form where you can specify the following:
-
+2. Once the CLI is running, enter `configure`, which will guide you through a step-by-step form where you can specify the following:
- `Listening port`
- Define the port for the built-in reverse proxy.
- *Default*: `80`
+ Define the port for the built-in reverse proxy.
+ _Default_: `80`
- `Max file-upload size`
- Set the maximum file size (in MB) that members can upload.
- *Default*: `5 MB`
+ Set the maximum file size (in MB) that members can upload.
+ _Default_: `5 MB`
- `External Postgres URL`
- Provide the URL of your external PostgreSQL instance if you want to switch from the default Plane configuration.
- *Default*: `Postgres 15.5` in the Docker container.
+ Provide the URL of your external PostgreSQL instance if you want to switch from the default Plane configuration.
+ _Default_: `Postgres 15.5` in the Docker container.
- ::: warning
- Don’t use a database on your local machine. If you use `localhost` in the URL, it won’t work. Make sure to use a database hosted on a network-accessible server.
+ ::: warning
+ Don’t use a database on your local machine. If you use `localhost` in the URL, it won’t work. Make sure to use a database hosted on a network-accessible server.
- Avoid using special characters in your PostgreSQL password.
- :::
+ Avoid using special characters in your PostgreSQL password.
+ :::
- `External Redis URL`
- Specify the URL of your external Redis instance to override the default Redis configuration.
- *Default*: `Redis 7.2.4`
+ Specify the URL of your external Redis instance to override the default Redis configuration.
+ _Default_: `Redis 7.2.4`
- - `External storage`
+ - `External storage`
Plane currently supports only S3 compatible storages.
- *Default*: `MinIO`
-
- 1. Ensure your IAM user has the following permissions on your S3 bucket.
- - **s3:GetObject**
- To access the objects.
- - **s3:PutObject**
- To upload new assets using the presigned url.
- 2. Configure the CORS policy on your bucket to enable presigned uploads. Use the example policy below, making sure to replace `` with your actual domain.
- ```
- [
- {
- "AllowedHeaders": [
- "*"
- ],
- "AllowedMethods": [
- "GET",
- "POST",
- "PUT",
- "DELETE",
- "HEAD"
- ],
- "AllowedOrigins": [
- "",
- ],
- "ExposeHeaders": [
- "ETag",
- "x-amz-server-side-encryption",
- "x-amz-request-id",
- "x-amz-id-2"
- ],
- "MaxAgeSeconds": 3000
- }
- ]
- ```
- 3. Switch to your external storage by providing the following values:
- - S3 access key IDÂ
- - S3 secret access key
- - S3 bucket name
- - S3 regionÂ
- - S3 endpoint URL
-
-3. After confirming your choices, your instance will automatically restart with the updated configuration.
+ _Default_: `MinIO`
+ 1. Ensure your IAM user has the following permissions on your S3 bucket.
+ - **s3:GetObject**
+ To access the objects.
+ - **s3:PutObject**
+ To upload new assets using the presigned url.
+ 2. Configure the CORS policy on your bucket to enable presigned uploads. Use the example policy below, making sure to replace `` with your actual domain.
+ ```
+ [
+ {
+ "AllowedHeaders": [
+ "*"
+ ],
+ "AllowedMethods": [
+ "GET",
+ "POST",
+ "PUT",
+ "DELETE",
+ "HEAD"
+ ],
+ "AllowedOrigins": [
+ "",
+ ],
+ "ExposeHeaders": [
+ "ETag",
+ "x-amz-server-side-encryption",
+ "x-amz-request-id",
+ "x-amz-id-2"
+ ],
+ "MaxAgeSeconds": 3000
+ }
+ ]
+ ```
+ 3. Switch to your external storage by providing the following values:
+ - S3 access key IDÂ
+ - S3 secret access key
+ - S3 bucket name
+ - S3 regionÂ
+ - S3 endpoint URL
+
+3. After confirming your choices, your instance will automatically restart with the updated configuration.
::: details Community Edition
@@ -93,6 +90,7 @@ To configure external Postgres, Redis, and S3 storage for the Plane Community Ed
1. Open the `plane.env` file on your server where Plane is installed.
2. In the **DB SETTINGS** section, update the variables to connect to your external Postgres instance:
+
```bash
# DB SETTINGS
PGHOST=your-external-postgres-host # Replace with the hostname or IP address of your Postgres server.
@@ -105,13 +103,14 @@ To configure external Postgres, Redis, and S3 storage for the Plane Community Ed
DATABASE_URL= # Leave this empty if you're providing values for the variables above. If you choose to use the DATABASE_URL, you can leave all the other database-related variables empty.
```
- ::: warning
- Don’t use a database on your local machine. If you use `localhost` in the URL, it won’t work. Make sure to use a database hosted on a network-accessible server.
+ ::: warning
+ Don’t use a database on your local machine. If you use `localhost` in the URL, it won’t work. Make sure to use a database hosted on a network-accessible server.
- Avoid using special characters in your PostgreSQL password.
- :::
+ Avoid using special characters in your PostgreSQL password.
+ :::
3. In the **REDIS SETTINGS** section, update the variables to connect to your external Redis instance:
+
```bash
# REDIS SETTINGS
REDIS_HOST=your-external-redis-host # Hostname or IP of the Redis server.
@@ -119,7 +118,7 @@ To configure external Postgres, Redis, and S3 storage for the Plane Community Ed
REDIS_URL= # Leave this empty if you're providing values for the variables above. If you choose to use the REDIS_URL, you can leave all the other redis-related variables empty.
```
-4. In the **DATA STORE SETTINGS** section, update the variables for any S3-compatible storage:
+4. In the **DATA STORE SETTINGS** section, update the variables for any S3-compatible storage:
```bash
# DATA STORE SETTINGS
USE_MINIO=0 # Set to 0 if using an external S3, 1 if using MinIO (default).
@@ -137,4 +136,4 @@ To configure external Postgres, Redis, and S3 storage for the Plane Community Ed
6. Restart Plane services to apply the new settings using the `setup.sh` script.
-:::
\ No newline at end of file
+:::
diff --git a/docs/self-hosting/govern/environment-variables.md b/docs/self-hosting/govern/environment-variables.md
index 190a57c9..f1a53e88 100644
--- a/docs/self-hosting/govern/environment-variables.md
+++ b/docs/self-hosting/govern/environment-variables.md
@@ -4,7 +4,6 @@ description: Configure environment variables for Plane. Complete reference of al
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Environment variables reference
This guide provides a comprehensive overview of all environment variables used in the Commercial Edition. These variables allow you to customize your Plane instance to best fit your organization's needs.
@@ -12,9 +11,9 @@ This guide provides a comprehensive overview of all environment variables used i
## Where to find the .env file
The environment file for Plane Commercial Edition is located at:
- ```bash
+`bash
/opt/plane/plane.env
- ```
+ `
This is where you'll make all configuration changes. Remember to restart the instance after making changes to ensure they take effect.
@@ -22,141 +21,141 @@ This is where you'll make all configuration changes. Remember to restart the ins
### General settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **INSTALL_DIR** | Directory where Plane is installed. | `/opt/plane` |
-| **DOMAIN_NAME** | Primary domain name for your Plane instance. This determines how users will access your installation. | `localhost` |
-| **APP_RELEASE_VERSION** | The version of Plane Commercial Edition you're running. This helps with troubleshooting and ensures compatibility. | *Current release version* |
-| **WEB_URL** | The complete base URL for the web application including protocol (e.g., `https://plane.example.com`).|`http://localhost`|
-| **CORS_ALLOWED_ORIGINS** | Comma-separated list of origins allowed to make cross-origin requests to your API. Usually, this should include your WEB_URL. |`http://localhost`|
-| **DEBUG** | Toggles debug mode for more verbose logging and debugging information.| `0` (disabled) |
+| Variable | Description | Default Value |
+| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
+| **INSTALL_DIR** | Directory where Plane is installed. | `/opt/plane` |
+| **DOMAIN_NAME** | Primary domain name for your Plane instance. This determines how users will access your installation. | `localhost` |
+| **APP_RELEASE_VERSION** | The version of Plane Commercial Edition you're running. This helps with troubleshooting and ensures compatibility. | _Current release version_ |
+| **WEB_URL** | The complete base URL for the web application including protocol (e.g., `https://plane.example.com`). | `http://localhost` |
+| **CORS_ALLOWED_ORIGINS** | Comma-separated list of origins allowed to make cross-origin requests to your API. Usually, this should include your WEB_URL. | `http://localhost` |
+| **DEBUG** | Toggles debug mode for more verbose logging and debugging information. | `0` (disabled) |
### Scaling and performance
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **WEB_REPLICAS** | Number of web server replicas for load balancing. | `1` |
-| **SPACE_REPLICAS** | Number of space service replicas for workspaces. | `1` |
-| **ADMIN_REPLICAS** | Number of admin service replicas. | `1` |
-| **API_REPLICAS** | Number of API service replicas. | `1` |
-| **WORKER_REPLICAS** | Number of worker service replicas for background tasks. | `1` |
-| **BEAT_WORKER_REPLICAS** | Number of beat worker replicas for scheduled tasks. | `1` |
-| **LIVE_REPLICAS** | Number of live service replicas for real-time updates. | `1` |
-| **GUNICORN_WORKERS** | Number of Gunicorn workers for handling web requests. Increase for better performance on high-traffic instances. | `2` |
+| Variable | Description | Default Value |
+| ------------------------ | ---------------------------------------------------------------------------------------------------------------- | ------------- |
+| **WEB_REPLICAS** | Number of web server replicas for load balancing. | `1` |
+| **SPACE_REPLICAS** | Number of space service replicas for workspaces. | `1` |
+| **ADMIN_REPLICAS** | Number of admin service replicas. | `1` |
+| **API_REPLICAS** | Number of API service replicas. | `1` |
+| **WORKER_REPLICAS** | Number of worker service replicas for background tasks. | `1` |
+| **BEAT_WORKER_REPLICAS** | Number of beat worker replicas for scheduled tasks. | `1` |
+| **LIVE_REPLICAS** | Number of live service replicas for real-time updates. | `1` |
+| **GUNICORN_WORKERS** | Number of Gunicorn workers for handling web requests. Increase for better performance on high-traffic instances. | `2` |
### Networking and security
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **LISTEN_HTTP_PORT** | Port for HTTP traffic. | `80` |
-| **LISTEN_HTTPS_PORT** | Port for HTTPS traffic. | `443` |
-| **APP_PROTOCOL** | Protocol to be used, either `http` or `https`. | `http` |
-| **TRUSTED_PROXIES** | CIDR notation of trusted proxies for request forwarding. Important when behind load balancers or reverse proxies. | `0.0.0.0/0` |
-| **SSL_VERIFY** | Whether to verify SSL certificates for outgoing connections. Set to `0` only in development environments. | `1` |
+| Variable | Description | Default Value |
+| --------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------- |
+| **LISTEN_HTTP_PORT** | Port for HTTP traffic. | `80` |
+| **LISTEN_HTTPS_PORT** | Port for HTTPS traffic. | `443` |
+| **APP_PROTOCOL** | Protocol to be used, either `http` or `https`. | `http` |
+| **TRUSTED_PROXIES** | CIDR notation of trusted proxies for request forwarding. Important when behind load balancers or reverse proxies. | `0.0.0.0/0` |
+| **SSL_VERIFY** | Whether to verify SSL certificates for outgoing connections. Set to `0` only in development environments. | `1` |
### SSL and certificates
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **CERT_EMAIL** | Email used for SSL certificate registration with Let's Encrypt or other ACME providers. |`admin@example.com`|
-| **CERT_ACME_CA** | ACME Certificate Authority URL for SSL certificate issuance. | `https://acme-v02.api.letsencrypt.org/directory` |
-| **CERT_ACME_DNS** | DNS provider configuration for SSL certificate domain validation. Format varies by provider. | |
-| **SITE_ADDRES** | The domain name and port required by Caddy for serving your Plane instance. This determines how Caddy will handle incoming requests. | `localhost:80` |
+| Variable | Description | Default Value |
+| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------ |
+| **CERT_EMAIL** | Email used for SSL certificate registration with Let's Encrypt or other ACME providers. | `admin@example.com` |
+| **CERT_ACME_CA** | ACME Certificate Authority URL for SSL certificate issuance. | `https://acme-v02.api.letsencrypt.org/directory` |
+| **CERT_ACME_DNS** | DNS provider configuration for SSL certificate domain validation. Format varies by provider. | |
+| **SITE_ADDRES** | The domain name and port required by Caddy for serving your Plane instance. This determines how Caddy will handle incoming requests. | `localhost:80` |
### Database settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **PGHOST** | Hostname or IP address of your PostgreSQL server. | `plane-db` |
-| **PGDATABASE** | Name of the PostgreSQL database Plane will use. | `plane` |
-| **POSTGRES_USER** | Username for PostgreSQL authentication. | `plane` |
-| **POSTGRES_PASSWORD** | Password for PostgreSQL authentication. **Critical:** Use a strong, unique password here. |`plane`|
-| **POSTGRES_DB** | Same as PGDATABASE - the name of the PostgreSQL database. | `plane` |
-| **POSTGRES_PORT** | TCP port your PostgreSQL server is listening on. | `5432` |
-| **PGDATA** | Directory path where PostgreSQL data is stored. Only relevant if you're managing PostgreSQL within the same container/system. | `/var/lib/postgresql/data` |
-| **DATABASE_URL** | Full connection string for PostgreSQL. If provided, this takes precedence over individual connection parameters. Format: `postgresql://username:password@host:port/dbname` | |
+| Variable | Description | Default Value |
+| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
+| **PGHOST** | Hostname or IP address of your PostgreSQL server. | `plane-db` |
+| **PGDATABASE** | Name of the PostgreSQL database Plane will use. | `plane` |
+| **POSTGRES_USER** | Username for PostgreSQL authentication. | `plane` |
+| **POSTGRES_PASSWORD** | Password for PostgreSQL authentication. **Critical:** Use a strong, unique password here. | `plane` |
+| **POSTGRES_DB** | Same as PGDATABASE - the name of the PostgreSQL database. | `plane` |
+| **POSTGRES_PORT** | TCP port your PostgreSQL server is listening on. | `5432` |
+| **PGDATA** | Directory path where PostgreSQL data is stored. Only relevant if you're managing PostgreSQL within the same container/system. | `/var/lib/postgresql/data` |
+| **DATABASE_URL** | Full connection string for PostgreSQL. If provided, this takes precedence over individual connection parameters. Format: `postgresql://username:password@host:port/dbname` | |
### Redis settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
+| Variable | Description | Default Value |
+| -------------- | -------------------------------------------- | ------------- |
| **REDIS_HOST** | Hostname or IP address of your Redis server. | `plane-redis` |
-| **REDIS_PORT** | TCP port your Redis server is listening on. | `6379` |
-| **REDIS_URL** | Full connection string for Redis.| |
+| **REDIS_PORT** | TCP port your Redis server is listening on. | `6379` |
+| **REDIS_URL** | Full connection string for Redis. | |
### RabbitMQ settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **RABBITMQ_HOST** | Hostname or IP address of your RabbitMQ server. | `plane-mq` |
-| **RABBITMQ_PORT** | TCP port your RabbitMQ server is listening on. | `5672` |
-| **RABBITMQ_DEFAULT_USER** | Username for RabbitMQ authentication. | `plane` |
-| **RABBITMQ_DEFAULT_PASS** | Password for RabbitMQ authentication. | `plane` |
-| **RABBITMQ_DEFAULT_VHOST** | Virtual host for RabbitMQ, providing logical separation of resources. | `plane` |
-| **AMQP_URL** | Full connection string for RabbitMQ. Format: `amqp://username:password@host:port/vhost` | |
+| Variable | Description | Default Value |
+| -------------------------- | --------------------------------------------------------------------------------------- | ------------- |
+| **RABBITMQ_HOST** | Hostname or IP address of your RabbitMQ server. | `plane-mq` |
+| **RABBITMQ_PORT** | TCP port your RabbitMQ server is listening on. | `5672` |
+| **RABBITMQ_DEFAULT_USER** | Username for RabbitMQ authentication. | `plane` |
+| **RABBITMQ_DEFAULT_PASS** | Password for RabbitMQ authentication. | `plane` |
+| **RABBITMQ_DEFAULT_VHOST** | Virtual host for RabbitMQ, providing logical separation of resources. | `plane` |
+| **AMQP_URL** | Full connection string for RabbitMQ. Format: `amqp://username:password@host:port/vhost` | |
### Authentication and security
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **SECRET_KEY** | Secret key used for various cryptographic operations, including JWT token signing. | |
-| **MACHINE_SIGNATURE** | Unique identifier for your instance, used for licensing and authentication.| |
+
+| Variable | Description | Default Value |
+| --------------------- | ---------------------------------------------------------------------------------- | ------------- |
+| **SECRET_KEY** | Secret key used for various cryptographic operations, including JWT token signing. | |
+| **MACHINE_SIGNATURE** | Unique identifier for your instance, used for licensing and authentication. | |
### File Storage (MinIO / S3)
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **USE_MINIO** | Determines whether to use MinIO for object storage. Set to `1` to enable MinIO, `0` to use configured S3 or local storage. | `1` |
-| **AWS_REGION** | AWS region for S3 storage services. | |
-| **AWS_ACCESS_KEY_ID** | Access key for MinIO or AWS S3 authentication. | |
-| **AWS_SECRET_ACCESS_KEY** | Secret key for MinIO or AWS S3 authentication. | |
-| **AWS_S3_ENDPOINT_URL** | Custom endpoint URL for MinIO or S3-compatible storage. | `http://plane-minio:9000`|
-| **AWS_S3_BUCKET_NAME** | S3 bucket name for file storage. | `uploads` |
-| **MINIO_ROOT_USER** | Username for MinIO authentication. This is effectively your MinIO admin account. | `access-key` |
-| **MINIO_ROOT_PASSWORD** | Password for MinIO root user authentication. Keep this secure as it provides full access to your storage. | `secret-key` |
-| **BUCKET_NAME** | S3 bucket name where all file uploads will be stored. This bucket will be automatically created if it doesn't exist. | `uploads` |
-| **FILE_SIZE_LIMIT** | Maximum file upload size in bytes. | `5242880` (5MB) |
-| **MINIO_ENDPOINT_SSL** | Force HTTPS for MinIO when dealing with SSL termination. Set to `1` to enable. | `0` |
+| Variable | Description | Default Value |
+| ------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
+| **USE_MINIO** | Determines whether to use MinIO for object storage. Set to `1` to enable MinIO, `0` to use configured S3 or local storage. | `1` |
+| **AWS_REGION** | AWS region for S3 storage services. | |
+| **AWS_ACCESS_KEY_ID** | Access key for MinIO or AWS S3 authentication. | |
+| **AWS_SECRET_ACCESS_KEY** | Secret key for MinIO or AWS S3 authentication. | |
+| **AWS_S3_ENDPOINT_URL** | Custom endpoint URL for MinIO or S3-compatible storage. | `http://plane-minio:9000` |
+| **AWS_S3_BUCKET_NAME** | S3 bucket name for file storage. | `uploads` |
+| **MINIO_ROOT_USER** | Username for MinIO authentication. This is effectively your MinIO admin account. | `access-key` |
+| **MINIO_ROOT_PASSWORD** | Password for MinIO root user authentication. Keep this secure as it provides full access to your storage. | `secret-key` |
+| **BUCKET_NAME** | S3 bucket name where all file uploads will be stored. This bucket will be automatically created if it doesn't exist. | `uploads` |
+| **FILE_SIZE_LIMIT** | Maximum file upload size in bytes. | `5242880` (5MB) |
+| **MINIO_ENDPOINT_SSL** | Force HTTPS for MinIO when dealing with SSL termination. Set to `1` to enable. | `0` |
### GitHub integration
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **GITHUB_CLIENT_ID** | OAuth client ID for GitHub integration. | |
-| **GITHUB_CLIENT_SECRET** | OAuth client secret for GitHub integration. | |
-| **GITHUB_APP_NAME** | GitHub App name for enhanced GitHub integration. | |
-| **GITHUB_APP_ID** | GitHub App ID for enhanced GitHub integration. | |
-| **GITHUB_PRIVATE_KEY** | Private key for GitHub App authentication. | |
+| Variable | Description | Default Value |
+| ------------------------ | ------------------------------------------------ | ------------- |
+| **GITHUB_CLIENT_ID** | OAuth client ID for GitHub integration. | |
+| **GITHUB_CLIENT_SECRET** | OAuth client secret for GitHub integration. | |
+| **GITHUB_APP_NAME** | GitHub App name for enhanced GitHub integration. | |
+| **GITHUB_APP_ID** | GitHub App ID for enhanced GitHub integration. | |
+| **GITHUB_PRIVATE_KEY** | Private key for GitHub App authentication. | |
### Slack integration
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **SLACK_CLIENT_ID** | OAuth client ID for Slack integration. | |
-| **SLACK_CLIENT_SECRET** | OAuth client secret for Slack integration. | |
+| Variable | Description | Default Value |
+| ----------------------- | ------------------------------------------ | ------------- |
+| **SLACK_CLIENT_ID** | OAuth client ID for Slack integration. | |
+| **SLACK_CLIENT_SECRET** | OAuth client secret for Slack integration. | |
### GitLab integration
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **GITLAB_CLIENT_ID** | OAuth client ID for GitLab integration. | |
-| **GITLAB_CLIENT_SECRET** | OAuth client secret for GitLab integration. | |
+| Variable | Description | Default Value |
+| ------------------------ | ------------------------------------------- | ------------- |
+| **GITLAB_CLIENT_ID** | OAuth client ID for GitLab integration. | |
+| **GITLAB_CLIENT_SECRET** | OAuth client secret for GitLab integration. | |
### OpenSearch
-| Variable | Description | Default Value |
-|----------|-------------|---------|
-| `OPENSEARCH_ENABLED` | Enable OpenSearch integration | `1` |
-| `OPENSEARCH_URL` | OpenSearch endpoint URL | `https://opensearch.example.com:9200/` |
-| `OPENSEARCH_USERNAME` | Authentication username | `admin` |
-| `OPENSEARCH_PASSWORD` | Authentication password | `your-secure-password` |
-| `OPENSEARCH_INDEX_PREFIX` | Prefix for all index names (useful for multi-tenant setups) | (empty) |
+| Variable | Description | Default Value |
+| ------------------------- | ----------------------------------------------------------- | -------------------------------------- |
+| `OPENSEARCH_ENABLED` | Enable OpenSearch integration | `1` |
+| `OPENSEARCH_URL` | OpenSearch endpoint URL | `https://opensearch.example.com:9200/` |
+| `OPENSEARCH_USERNAME` | Authentication username | `admin` |
+| `OPENSEARCH_PASSWORD` | Authentication password | `your-secure-password` |
+| `OPENSEARCH_INDEX_PREFIX` | Prefix for all index names (useful for multi-tenant setups) | (empty) |
### API settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **API_KEY_RATE_LIMIT** | Rate limit for API requests to prevent abuse. Format: `number/timeunit` | `60/minute` |
-
+| Variable | Description | Default Value |
+| ---------------------- | ----------------------------------------------------------------------- | ------------- |
+| **API_KEY_RATE_LIMIT** | Rate limit for API requests to prevent abuse. Format: `number/timeunit` | `60/minute` |
::: details Community Edition
@@ -165,93 +164,92 @@ This guide provides a comprehensive overview of all environment variables availa
## Where to find the environment file
The environment configuration file is located at:
- ```bash
+`bash
plane-selfhost/plane-app/plane.env
- ```
+ `
## Environment Variables
### General settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **APP_DOMAIN** | Domain name for your Plane instance. This determines how users will access your installation. | `localhost` |
-| **APP_RELEASE** | Release version of Plane. Helps with compatibility and troubleshooting. | `stable` |
-| **WEB_URL** | The complete base URL for the web application including protocol. Essential for email links and integrations. | `http://${APP_DOMAIN}` |
-| **CORS_ALLOWED_ORIGINS** | Comma-separated list of origins allowed to make cross-origin requests to your API. | `http://${APP_DOMAIN}` |
-| **DEBUG** | Toggles debug mode for verbose logging. Set to `1` to enable, `0` to disable. Not recommended in production as it may expose sensitive information. | `0` |
-| **LISTEN_HTTP_PORT** | Port for HTTP traffic. The primary port your users will connect to. | `80` |
-| **LISTEN_HTTPS_PORT** | Port for HTTPS traffic. The primary port your users will connect to. | `443` |
+| Variable | Description | Default Value |
+| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
+| **APP_DOMAIN** | Domain name for your Plane instance. This determines how users will access your installation. | `localhost` |
+| **APP_RELEASE** | Release version of Plane. Helps with compatibility and troubleshooting. | `stable` |
+| **WEB_URL** | The complete base URL for the web application including protocol. Essential for email links and integrations. | `http://${APP_DOMAIN}` |
+| **CORS_ALLOWED_ORIGINS** | Comma-separated list of origins allowed to make cross-origin requests to your API. | `http://${APP_DOMAIN}` |
+| **DEBUG** | Toggles debug mode for verbose logging. Set to `1` to enable, `0` to disable. Not recommended in production as it may expose sensitive information. | `0` |
+| **LISTEN_HTTP_PORT** | Port for HTTP traffic. The primary port your users will connect to. | `80` |
+| **LISTEN_HTTPS_PORT** | Port for HTTPS traffic. The primary port your users will connect to. | `443` |
### Scaling and performance
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **WEB_REPLICAS** | Number of web server replicas for serving the frontend UI. Increase for better load distribution. | `1` |
-| **SPACE_REPLICAS** | Number of space service replicas handling workspace-related operations. | `1` |
-| **ADMIN_REPLICAS** | Number of admin service replicas for administrative functions. | `1` |
-| **API_REPLICAS** | Number of API service replicas processing API requests. | `1` |
-| **WORKER_REPLICAS** | Number of worker service replicas handling background tasks. | `1` |
-| **BEAT_WORKER_REPLICAS** | Number of beat worker replicas for scheduled/periodic tasks. | `1` |
-| **LIVE_REPLICAS** | Number of live service replicas for real-time updates and WebSocket connections. | `1` |
-| **GUNICORN_WORKERS** | Number of Gunicorn workers per API instance. Increase for better request handling capacity. | `1` |
+| Variable | Description | Default Value |
+| ------------------------ | ------------------------------------------------------------------------------------------------- | ------------- |
+| **WEB_REPLICAS** | Number of web server replicas for serving the frontend UI. Increase for better load distribution. | `1` |
+| **SPACE_REPLICAS** | Number of space service replicas handling workspace-related operations. | `1` |
+| **ADMIN_REPLICAS** | Number of admin service replicas for administrative functions. | `1` |
+| **API_REPLICAS** | Number of API service replicas processing API requests. | `1` |
+| **WORKER_REPLICAS** | Number of worker service replicas handling background tasks. | `1` |
+| **BEAT_WORKER_REPLICAS** | Number of beat worker replicas for scheduled/periodic tasks. | `1` |
+| **LIVE_REPLICAS** | Number of live service replicas for real-time updates and WebSocket connections. | `1` |
+| **GUNICORN_WORKERS** | Number of Gunicorn workers per API instance. Increase for better request handling capacity. | `1` |
### API settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **API_KEY_RATE_LIMIT** | Rate limit for API requests to prevent abuse. Format: `number/timeunit` | `60/minute` |
+| Variable | Description | Default Value |
+| ---------------------- | ----------------------------------------------------------------------- | ------------- |
+| **API_KEY_RATE_LIMIT** | Rate limit for API requests to prevent abuse. Format: `number/timeunit` | `60/minute` |
### Database settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **PGHOST** | Hostname or IP address of your PostgreSQL server. | `plane-db` |
-| **PGDATABASE** | Name of the PostgreSQL database Plane will use. | `plane` |
-| **POSTGRES_USER** | Username for PostgreSQL authentication. | `plane` |
-| **POSTGRES_PASSWORD** | Password for PostgreSQL authentication. Use a strong, unique password. | `plane` |
-| **POSTGRES_DB** | Same as PGDATABASE - the name of the PostgreSQL database. | `plane` |
-| **POSTGRES_PORT** | TCP port your PostgreSQL server is listening on. | `5432` |
-| **PGDATA** | Directory path where PostgreSQL data is stored. Only relevant if you're managing PostgreSQL directly. | `/var/lib/postgresql/data` |
-| **DATABASE_URL** | Full connection string for PostgreSQL. If provided, overrides individual settings. Format: `postgresql://username:password@host:port/dbname` | |
+| Variable | Description | Default Value |
+| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
+| **PGHOST** | Hostname or IP address of your PostgreSQL server. | `plane-db` |
+| **PGDATABASE** | Name of the PostgreSQL database Plane will use. | `plane` |
+| **POSTGRES_USER** | Username for PostgreSQL authentication. | `plane` |
+| **POSTGRES_PASSWORD** | Password for PostgreSQL authentication. Use a strong, unique password. | `plane` |
+| **POSTGRES_DB** | Same as PGDATABASE - the name of the PostgreSQL database. | `plane` |
+| **POSTGRES_PORT** | TCP port your PostgreSQL server is listening on. | `5432` |
+| **PGDATA** | Directory path where PostgreSQL data is stored. Only relevant if you're managing PostgreSQL directly. | `/var/lib/postgresql/data` |
+| **DATABASE_URL** | Full connection string for PostgreSQL. If provided, overrides individual settings. Format: `postgresql://username:password@host:port/dbname` | |
### Redis settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **REDIS_HOST** | Hostname or IP address of your Redis server. | `plane-redis` |
-| **REDIS_PORT** | TCP port your Redis server is listening on. | `6379` |
-| **REDIS_URL** | Full connection string for Redis. Format: `redis://username:password@host:port` | |
+| Variable | Description | Default Value |
+| -------------- | ------------------------------------------------------------------------------- | ------------- |
+| **REDIS_HOST** | Hostname or IP address of your Redis server. | `plane-redis` |
+| **REDIS_PORT** | TCP port your Redis server is listening on. | `6379` |
+| **REDIS_URL** | Full connection string for Redis. Format: `redis://username:password@host:port` | |
### RabbitMQ settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **RABBITMQ_HOST** | Hostname or IP address of your RabbitMQ server. | `plane-mq` |
-| **RABBITMQ_PORT** | TCP port your RabbitMQ server is listening on. | `5672` |
-| **RABBITMQ_USER** | Username for RabbitMQ authentication. | `plane` |
-| **RABBITMQ_PASSWORD** | Password for RabbitMQ authentication. Use a strong, unique password. | `plane` |
-| **RABBITMQ_VHOST** | Virtual host for RabbitMQ, providing logical separation of resources. | `plane` |
-| **AMQP_URL** | Full connection string for RabbitMQ. If not provided, it's constructed from individual settings. | `amqp://plane:plane@plane-mq:5672/plane` |
+| Variable | Description | Default Value |
+| --------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------- |
+| **RABBITMQ_HOST** | Hostname or IP address of your RabbitMQ server. | `plane-mq` |
+| **RABBITMQ_PORT** | TCP port your RabbitMQ server is listening on. | `5672` |
+| **RABBITMQ_USER** | Username for RabbitMQ authentication. | `plane` |
+| **RABBITMQ_PASSWORD** | Password for RabbitMQ authentication. Use a strong, unique password. | `plane` |
+| **RABBITMQ_VHOST** | Virtual host for RabbitMQ, providing logical separation of resources. | `plane` |
+| **AMQP_URL** | Full connection string for RabbitMQ. If not provided, it's constructed from individual settings. | `amqp://plane:plane@plane-mq:5672/plane` |
### File Storage (MinIO / S3)
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **USE_MINIO** | Whether to use MinIO for object storage. Set to `1` to enable, `0` to use other configured storage. | `1` |
-| **MINIO_ENDPOINT_SSL** | Force HTTPS for MinIO when handling SSL termination. Set to `1` to enable. | `0` |
-| **AWS_REGION** | AWS region for S3 storage services. Applies when using S3 or MinIO. | |
-| **AWS_ACCESS_KEY_ID** | Access key for MinIO or AWS S3 authentication. | `access-key` |
-| **AWS_SECRET_ACCESS_KEY** | Secret key for MinIO or AWS S3 authentication. | `secret-key` |
-| **AWS_S3_ENDPOINT_URL** | Endpoint URL for MinIO or S3-compatible storage. | |
-| **AWS_S3_BUCKET_NAME** | S3 bucket name for file storage. All uploads will be stored in this bucket. | `uploads` |
-| **FILE_SIZE_LIMIT** | Maximum file upload size in bytes. | `5242880` (5MB) |
-
+| Variable | Description | Default Value |
+| ------------------------- | --------------------------------------------------------------------------------------------------- | --------------- |
+| **USE_MINIO** | Whether to use MinIO for object storage. Set to `1` to enable, `0` to use other configured storage. | `1` |
+| **MINIO_ENDPOINT_SSL** | Force HTTPS for MinIO when handling SSL termination. Set to `1` to enable. | `0` |
+| **AWS_REGION** | AWS region for S3 storage services. Applies when using S3 or MinIO. | |
+| **AWS_ACCESS_KEY_ID** | Access key for MinIO or AWS S3 authentication. | `access-key` |
+| **AWS_SECRET_ACCESS_KEY** | Secret key for MinIO or AWS S3 authentication. | `secret-key` |
+| **AWS_S3_ENDPOINT_URL** | Endpoint URL for MinIO or S3-compatible storage. | |
+| **AWS_S3_BUCKET_NAME** | S3 bucket name for file storage. All uploads will be stored in this bucket. | `uploads` |
+| **FILE_SIZE_LIMIT** | Maximum file upload size in bytes. | `5242880` (5MB) |
### Security settings
-| Variable | Description | Default Value |
-|----------|-------------|---------------|
-| **SECRET_KEY** | Secret key used for cryptographic operations like session handling and token generation. Should be a long, random string. | |
+| Variable | Description | Default Value |
+| -------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------- |
+| **SECRET_KEY** | Secret key used for cryptographic operations like session handling and token generation. Should be a long, random string. | |
-:::
\ No newline at end of file
+:::
diff --git a/docs/self-hosting/govern/external-secrets.md b/docs/self-hosting/govern/external-secrets.md
index 4c917a7e..cdc8a793 100644
--- a/docs/self-hosting/govern/external-secrets.md
+++ b/docs/self-hosting/govern/external-secrets.md
@@ -4,7 +4,6 @@ description: Learn how to configure external secrets for kubernetes deployments
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure external secrets for Kubernetes deployments
This guide explains how to integrate Plane with external secret management solutions, enabling secure and centralized management of sensitive configuration data. The examples provided cover AWS Secrets Manager and HashiCorp Vault integrations, but you can adapt these patterns to your preferred secret management solution.
@@ -17,87 +16,89 @@ This guide explains how to integrate Plane with external secret management solut
4. Create IAM policy (e.g., `external-secret-access-policy`) with the following JSON:
- ```json
- {
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Action": [
- "secretsmanager:GetResourcePolicy",
- "secretsmanager:GetSecretValue",
- "secretsmanager:DescribeSecret",
- "secretsmanager:ListSecretVersionIds"
- ],
- "Resource": [
- "arn:aws:secretsmanager:::secret:*"
- ]
- }
- ]
- }
- ```
- Replace `` and `` with your AWS region and account ID.
+ ```json
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": [
+ "secretsmanager:GetResourcePolicy",
+ "secretsmanager:GetSecretValue",
+ "secretsmanager:DescribeSecret",
+ "secretsmanager:ListSecretVersionIds"
+ ],
+ "Resource": ["arn:aws:secretsmanager:::secret:*"]
+ }
+ ]
+ }
+ ```
+
+ Replace `` and `` with your AWS region and account ID.
5. Create IAM role (e.g., external-secret-access-role) with the following trust relationship:
- ```json
- {
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Principal": {
- "AWS": ""
- },
- "Action": "sts:AssumeRole"
- }
- ]
- }
- ```
-
- Replace `` with the ARN of the user created in step 1.
+ ```json
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": ""
+ },
+ "Action": "sts:AssumeRole"
+ }
+ ]
+ }
+ ```
+
+ Replace `` with the ARN of the user created in step 1.
6. Attach the AWS IAM policy created in step 4 to the IAM role.
-7. Create secrets in AWS Secrets Manager with your Plane configuration values. For example, store RabbitMQ credentials with a name like `prod/secrets/rabbitmq`.
+7. Create secrets in AWS Secrets Manager with your Plane configuration values. For example, store RabbitMQ credentials with a name like `prod/secrets/rabbitmq`.
- |Key|Value|
- |-------|--------|
- |RABBITMQ_DEFAULT_USER|plane|
- |RABBITMQ_DEFAULT_PASS|plane123|
+ | Key | Value |
+ | --------------------- | -------- |
+ | RABBITMQ_DEFAULT_USER | plane |
+ | RABBITMQ_DEFAULT_PASS | plane123 |
- Follow this pattern to manage all the [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in AWS Secrets Manager.
+ Follow this pattern to manage all the [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in AWS Secrets Manager.
8. Create a Kubernetes secret containing AWS credentials in your application namespace:
- ```sh
- kubectl create secret generic aws-creds-secret \
- --from-literal=access-key= \
- --from-literal=secret-access-key= \
- -n
- ```
+
+ ```sh
+ kubectl create secret generic aws-creds-secret \
+ --from-literal=access-key= \
+ --from-literal=secret-access-key= \
+ -n
+ ```
9. Apply the following YAML to create a ClusterSecretStore resource:
- ```yaml
- apiVersion: external-secrets.io/v1
- kind: ClusterSecretStore
- metadata:
- name: cluster-aws-secretsmanager
- namespace:
- spec:
- provider:
- aws:
- service: SecretsManager
- role: arn:aws:iam:::role/
- region: eu-west-1
- auth:
- accessKeyIDSecretRef:
- name: aws-creds-secret
- key: access-key
- secretAccessKeySecretRef:
- name: aws-creds-secret
- key: secret-access-key
- ```
- Replace `` and `` with your AWS account ID and the role name created in Step 5.
+
+ ```yaml
+ apiVersion: external-secrets.io/v1
+ kind: ClusterSecretStore
+ metadata:
+ name: cluster-aws-secretsmanager
+ namespace:
+ spec:
+ provider:
+ aws:
+ service: SecretsManager
+ role: arn:aws:iam:::role/
+ region: eu-west-1
+ auth:
+ accessKeyIDSecretRef:
+ name: aws-creds-secret
+ key: access-key
+ secretAccessKeySecretRef:
+ name: aws-creds-secret
+ key: secret-access-key
+ ```
+
+ Replace `` and `` with your AWS account ID and the role name created in Step 5.
10. Create an ExternalSecret resource to fetch secrets from AWS and create a corresponding Kubernetes secret:
```yaml
@@ -115,13 +116,13 @@ This guide explains how to integrate Plane with external secret management solut
name: rabbitmq-secret # Target Kubernetes secret name
creationPolicy: Owner
data:
- - secretKey: RABBITMQ_DEFAULT_USER
- remoteRef:
- key: prod/secrets/rabbitmq
- property: RABBITMQ_DEFAULT_USER
- - secretKey: RABBITMQ_DEFAULT_PASS
- remoteRef:
- key: prod/secrets/rabbitmq
+ - secretKey: RABBITMQ_DEFAULT_USER
+ remoteRef:
+ key: prod/secrets/rabbitmq
+ property: RABBITMQ_DEFAULT_USER
+ - secretKey: RABBITMQ_DEFAULT_PASS
+ remoteRef:
+ key: prod/secrets/rabbitmq
```
Make sure to set all [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in the AWS Secrets Manager, and then access them via ExternalSecret resources in your Kubernetes cluster.
@@ -134,62 +135,64 @@ Make sure to set all [environment variables](/self-hosting/methods/kubernetes#ex
3. Create a secret with your Plane configuration values (e.g., `secrets/rabbitmq_secrets`). For this example, we're setting up RabbitMQ credentials:
- |Key|Value|
- |-------|--------|
- |RABBITMQ_DEFAULT_USER|plane|
- |RABBITMQ_DEFAULT_PASS|plane123|
+ | Key | Value |
+ | --------------------- | -------- |
+ | RABBITMQ_DEFAULT_USER | plane |
+ | RABBITMQ_DEFAULT_PASS | plane123 |
- Follow this pattern to manage all the other [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in the Vault.
+ Follow this pattern to manage all the other [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in the Vault.
4. Create a Kubernetes secret containing your Vault token in your application namespace:
- ```sh
- kubectl create secret generic vault-token -n --from-literal=token=
- ```
+
+ ```sh
+ kubectl create secret generic vault-token -n --from-literal=token=
+ ```
5. Apply the following YAML to create a ClusterSecretStore resource:
- ```yaml
- apiVersion: external-secrets.io/v1
- kind: ClusterSecretStore
- metadata:
- name: vault-backend
- namespace:
- spec:
- provider:
- vault:
- server: "https://" # the address of your vault instance
- path: "secrets" # path for accessing the secrets
- version: "v2" # Vault API version
- auth:
- tokenSecretRef:
- name: "vault-token" # Use a k8s secret called vault-token
- key: "token" # Use this key to access the vault token
- ```
- Replace `` with your Vault server address.
+ ```yaml
+ apiVersion: external-secrets.io/v1
+ kind: ClusterSecretStore
+ metadata:
+ name: vault-backend
+ namespace:
+ spec:
+ provider:
+ vault:
+ server: 'https://' # the address of your vault instance
+ path: 'secrets' # path for accessing the secrets
+ version: 'v2' # Vault API version
+ auth:
+ tokenSecretRef:
+ name: 'vault-token' # Use a k8s secret called vault-token
+ key: 'token' # Use this key to access the vault token
+ ```
+
+ Replace `` with your Vault server address.
6. Create an ExternalSecret resource to fetch secrets from Vault and create a corresponding Kubernetes secret:
- ```yaml
- apiVersion: external-secrets.io/v1
- kind: ExternalSecret
- metadata:
- name: rabbitmq-external-secrets
- namespace: # application-namespace
- spec:
- refreshInterval: "1m"
- secretStoreRef:
- name: vault-backend # ClusterSecretStore name
- kind: ClusterSecretStore
- target:
- name: rabbitmq-secret # Target Kubernetes secret name
- creationPolicy: Owner
- data:
- - secretKey: RABBITMQ_DEFAULT_USER
- remoteRef:
- key: secrets/data/rabbitmq_secrets
- property: RABBITMQ_DEFAULT_USER
- - secretKey: RABBITMQ_DEFAULT_PASS
- remoteRef:
- key: secrets/data/rabbitmq_secrets
- ```
-
-Follow this pattern to manage all the environment variables in the Vault, then access them via ExternalSecret resources in your Kubernetes cluster.
\ No newline at end of file
+ ```yaml
+ apiVersion: external-secrets.io/v1
+ kind: ExternalSecret
+ metadata:
+ name: rabbitmq-external-secrets
+ namespace: # application-namespace
+ spec:
+ refreshInterval: '1m'
+ secretStoreRef:
+ name: vault-backend # ClusterSecretStore name
+ kind: ClusterSecretStore
+ target:
+ name: rabbitmq-secret # Target Kubernetes secret name
+ creationPolicy: Owner
+ data:
+ - secretKey: RABBITMQ_DEFAULT_USER
+ remoteRef:
+ key: secrets/data/rabbitmq_secrets
+ property: RABBITMQ_DEFAULT_USER
+ - secretKey: RABBITMQ_DEFAULT_PASS
+ remoteRef:
+ key: secrets/data/rabbitmq_secrets
+ ```
+
+Follow this pattern to manage all the environment variables in the Vault, then access them via ExternalSecret resources in your Kubernetes cluster.
diff --git a/docs/self-hosting/govern/github-oauth.md b/docs/self-hosting/govern/github-oauth.md
index a9a740f6..2888b8b8 100644
--- a/docs/self-hosting/govern/github-oauth.md
+++ b/docs/self-hosting/govern/github-oauth.md
@@ -4,7 +4,6 @@ description: Configure GitHub OAuth authentication for Plane. Enable GitHub sign
keywords: plane, self-hosting, deployment, plane installation, authentication, sso, oauth, configuration, administration
---
-
# Github OAuth
Plane supports GitHub OAuth so your users can sign-in with GitHub instead.
@@ -33,4 +32,4 @@ Plane supports GitHub OAuth so your users can sign-in with GitHub instead.
2. Add the client ID + the client secret from the GitHub app you just registered.
3. Click `Save `.
-Your Plane instance should now work with GitHub sign-in.
\ No newline at end of file
+Your Plane instance should now work with GitHub sign-in.
diff --git a/docs/self-hosting/govern/google-oauth.md b/docs/self-hosting/govern/google-oauth.md
index 6be9e673..a219deae 100644
--- a/docs/self-hosting/govern/google-oauth.md
+++ b/docs/self-hosting/govern/google-oauth.md
@@ -4,7 +4,6 @@ description: Setup Google OAuth authentication for Plane. Step-by-step guide to
keywords: plane, self-hosting, deployment, plane installation, authentication, sso, oauth, configuration, administration
---
-
# Google OAuth
Plane already ships with out-of-the-box support for Google OAuth. This is the easiest option to configure for Google Workspace users.
@@ -27,9 +26,10 @@ First, you will need to identify Plane as an approved OAuth app to Google.
Append the path that users should be redirected to after they have authenticated with Google. `https:///auth/google/callback` and `https:///auth/mobile/google/callback/` where `` is your self-hosted instance's domain.
3. Click **Create**.
4. Get the Client ID and Client secret under **OAuth 2.0 Client IDs** on the **Credentials** screen.
- 
+ 
## Configure Plane
+

1. Go to `Google` on the Authentication screen of `/god mode`.
@@ -38,5 +38,4 @@ First, you will need to identify Plane as an approved OAuth app to Google.
Your Plane instance should now work with `Sign in with Google`.
-
-::: infoWe don't restrict domains in with Google OAuth yet. It's on our roadmap.:::
\ No newline at end of file
+::: infoWe don't restrict domains in with Google OAuth yet. It's on our roadmap.:::
diff --git a/docs/self-hosting/govern/instance-admin.md b/docs/self-hosting/govern/instance-admin.md
index da53923b..963f4e82 100644
--- a/docs/self-hosting/govern/instance-admin.md
+++ b/docs/self-hosting/govern/instance-admin.md
@@ -4,7 +4,6 @@ description: Configure Plane instance admin settings. Learn about God mode and a
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Instance admin and God mode
An instance is a single self-managed installation of Plane on a private cloud or server that the `Instance admin` controls and administers. A single instance can house multiple workspaces.
@@ -24,53 +23,57 @@ New instances allow skipping going to God Mode and setting up your workspace ins
God Mode features a few screens as shown below.
-### General
+### General
+
The General settings page allows you to view or configure core instance details and telemetry preferences.
Here’s what you can manage:
- **Name of instance**
-Customize the name of your instance.
+ Customize the name of your instance.
- **Email**
-Displays the instance admin email address.
+ Displays the instance admin email address.
- **Instance ID**
-Displays a unique identifier for your instance.
+ Displays a unique identifier for your instance.
- **Chat with us**
-Enable or disable in-app chat support for users. Disabling telemetry automatically turns this off.
+ Enable or disable in-app chat support for users. Disabling telemetry automatically turns this off.
- **Let Plane collect anonymous usage data**
-Plane collects anonymized usage data (no PII) to help improve features and overall experience. You can turn this off anytime. See [Telemetry](/self-hosting/telemetry) for more info.
+ Plane collects anonymized usage data (no PII) to help improve features and overall experience. You can turn this off anytime. See [Telemetry](/self-hosting/telemetry) for more info.

### Workspaces
+
The Workspaces section allows you to manage all workspaces within your Plane instance.
-- **View all Workspaces**
-Access a complete list of workspaces on your instance.
+- **View all Workspaces**
+ Access a complete list of workspaces on your instance.
- **Create Workspaces**
-You can create new workspaces directly from this section. If workspace creation is restricted, only the instance admin will have this ability.
+ You can create new workspaces directly from this section. If workspace creation is restricted, only the instance admin will have this ability.
-- **Restrict Workspace creation**
-Toggle the **Prevent anyone from creating a workspace** option to prevent anyone else from creating workspaces. Once enabled, only you (the instance admin) can create new workspaces.
+- **Restrict Workspace creation**
+ Toggle the **Prevent anyone from creating a workspace** option to prevent anyone else from creating workspaces. Once enabled, only you (the instance admin) can create new workspaces.
To add users to a workspace, you will need to [invite them](https://docs.plane.so/core-concepts/workspaces/members#add-member) after creating it.
-
+
::: info
Workspace deletion is currently not supported.
:::

-### Email
+### Email
+
Set up your SMTP server here so you can send essential emails—password resets, exports, changes to your instance—and Plane-enabled emails—onboarding, tips and tricks, new features— to all your users. [Learn more here](/self-hosting/govern/communication).

### Authentication
+
Control what SSO and OAuth services your users can use to sign up and log in to your Plane instance. You can also toggle unique code and password logins on and off from here. [Learn more here](/self-hosting/govern/authentication).
- **Allow anyone to sign up without an invite**
@@ -95,59 +98,61 @@ You can use your own third-party libraries to update images in project settings.

## Add instance admin
+
To grant a user full administrative access (including God mode) to your self-hosted Plane instance, you'll need to assign them the Instance Admin role. Instance admins have unrestricted control over all configurations and settings within the instance.
To promote an existing user to Instance Admin, execute the following command in your terminal:
+
```bash
docker exec /bin/bash -c "python manage.py create_instance_admin "
-```
+```
+
Ensure that the provided email matches the user's registered account. This command will instantly elevate their privileges to full administrative access.
---------
+---
Soon, we will introduce God Mode for our Cloud users as well so they can manage their workspaces better. To get notified about this, [sign up here](https://ece39166.sibforms.com/serve/MUIFANgaMWIARsq1n0lMNrch19pdY2HJm9FkSXAeq1DrCoXJBmO9Yq6SPgtzu7rL0lQBmCvvz2A2arVl5WaDxYu6YhNW4PKNAis0DMXmpRnwm5633BvXqIYILqZuyqYiGS7_QjJ0Ozh4R2uctd8RwiiTLSHWpnV2njQt6DPV5cVr8FH3K-TouNAlBScOJxbCpjj8fYo2ULsEJeAL).
## FAQs
- ::: details How do you know who an Instance admin is?
- Whoever spins up the instance or upgrades to v0.14, we assume, is the instance admin. When you see Let's secure your instance, enter your email-password combo. If you are already using Plane with those credentials, you will be logged in and will see /god-mode features. If not, we will create a new user on your local instance and you will see /god-mode.
+::: details How do you know who an Instance admin is?
+Whoever spins up the instance or upgrades to v0.14, we assume, is the instance admin. When you see Let's secure your instance, enter your email-password combo. If you are already using Plane with those credentials, you will be logged in and will see /god-mode features. If not, we will create a new user on your local instance and you will see /god-mode.
- Our shrewd guess right now is users are technical enough to upgrade to or bring up a new instance with v0.14 are instance admins. If there’s a case where this isn’t true, please reach out to us before you upgrade or set up your fresh instance.
- :::
-
- ::: details What if I don’t complete secure instance set-up at the time of the upgrade?
- We strongly recommend completing set-up at upgrade so your regular users can access Plane without trouble. Because we are introducing several sensitive admin features in `God Mode`, we will show an instance-not-set-up screen to your regular users until such a time that you can complete the setup.
- 
- :::
+Our shrewd guess right now is users are technical enough to upgrade to or bring up a new instance with v0.14 are instance admins. If there’s a case where this isn’t true, please reach out to us before you upgrade or set up your fresh instance.
+:::
- ::: details What has changed with how existing regular users of my instance log in?
- All existing users will log in with their usual email address-password combos if they are already doing it. If they haven’t been using a password when not OAuthing into Plane, they will now need to. If OAuth is enabled, users can continue using your OAuth methods. New users will need to choose a password or OAuth into Plane.
- :::
+::: details What if I don’t complete secure instance set-up at the time of the upgrade?
+We strongly recommend completing set-up at upgrade so your regular users can access Plane without trouble. Because we are introducing several sensitive admin features in `God Mode`, we will show an instance-not-set-up screen to your regular users until such a time that you can complete the setup.
+
+:::
- ::: details What will happen to the default captain@plane.so account that you shipped so far?
- For all new instances, there won’t be a `captain@plane.so` account. Instance set-up will allow you to set up a workspace and set workspace and project admins.
+::: details What has changed with how existing regular users of my instance log in?
+All existing users will log in with their usual email address-password combos if they are already doing it. If they haven’t been using a password when not OAuthing into Plane, they will now need to. If OAuth is enabled, users can continue using your OAuth methods. New users will need to choose a password or OAuth into Plane.
+:::
- For existing instances, the instance admin’s email will be added to each project with the same permissions as `captain@plane.so’s` so you can remove that email completely from your workspaces and projects.
- :::
+::: details What will happen to the default captain@plane.so account that you shipped so far?
+For all new instances, there won’t be a `captain@plane.so` account. Instance set-up will allow you to set up a workspace and set workspace and project admins.
- ::: details This is unreal, but I have an instance that has a /god-mode path already. I can’t access my Plane instance. Help!
- That is unreal! Please reach out to us immediately on [support](https://discord.com/login?redirect_to=%2Fchannels%2F1031547764020084846%2F1094927053867995176) or on our [Discord](https://discord.com/invite/A92xrEGCge) and mark your message urgent. We will help you get your instance back pronto.
+For existing instances, the instance admin’s email will be added to each project with the same permissions as `captain@plane.so’s` so you can remove that email completely from your workspaces and projects.
+:::
- :::
+::: details This is unreal, but I have an instance that has a /god-mode path already. I can’t access my Plane instance. Help!
+That is unreal! Please reach out to us immediately on [support](https://discord.com/login?redirect_to=%2Fchannels%2F1031547764020084846%2F1094927053867995176) or on our [Discord](https://discord.com/invite/A92xrEGCge) and mark your message urgent. We will help you get your instance back pronto.
- ::: details How will emails for password resets and onboarding be sent to users of my instance(s)?
- We have always let you configure your own SMTP server to send emails from within your instance. It’s also why we are being deliberate about leading the instance admin of an existing instance to `/god-mode` first. After completing secure instance set-up now, you can configure your SMTP server on the UI instead of via `.env` variables. We strongly recommend you do that to avoid password-reset failures and failures in email delivery.
+:::
- Please [reach out](https://discord.com/login?redirect_to=%2Fchannels%2F1031547764020084846%2F1094927053867995176) to us on [Discord](https://discord.com/invite/A92xrEGCge) if you haven’t set up SMTP and are facing troubles with your users logging in.
- :::
+::: details How will emails for password resets and onboarding be sent to users of my instance(s)?
+We have always let you configure your own SMTP server to send emails from within your instance. It’s also why we are being deliberate about leading the instance admin of an existing instance to `/god-mode` first. After completing secure instance set-up now, you can configure your SMTP server on the UI instead of via `.env` variables. We strongly recommend you do that to avoid password-reset failures and failures in email delivery.
- ::: details Why are you introducing passwords for app.plane.so users? What’s happening with unique links to sign up and sign in?
- Unique links are secure and relatively easier, but we have heard from enough of our Cloud users that they would like to log in using a more permanent and easier method. Should you want to continue using unique codes, you are covered. We will keep that option alive for good.
+Please [reach out](https://discord.com/login?redirect_to=%2Fchannels%2F1031547764020084846%2F1094927053867995176) to us on [Discord](https://discord.com/invite/A92xrEGCge) if you haven’t set up SMTP and are facing troubles with your users logging in.
+:::
- While using Google or GitHub are good options already, not all of you would want to use them. For those that prefer a password and would like to do away with codes, we want to make that option available.
- :::
+::: details Why are you introducing passwords for app.plane.so users? What’s happening with unique links to sign up and sign in?
+Unique links are secure and relatively easier, but we have heard from enough of our Cloud users that they would like to log in using a more permanent and easier method. Should you want to continue using unique codes, you are covered. We will keep that option alive for good.
- ::: details Is there a God Mode for Cloud admins, too?
- Not now, but soon enough, there will be a `God Mode` for Cloud admins.
- :::
+While using Google or GitHub are good options already, not all of you would want to use them. For those that prefer a password and would like to do away with codes, we want to make that option available.
+:::
+::: details Is there a God Mode for Cloud admins, too?
+Not now, but soon enough, there will be a `God Mode` for Cloud admins.
+:::
diff --git a/docs/self-hosting/govern/integrations/github.md b/docs/self-hosting/govern/integrations/github.md
index 87a58883..68c2310b 100644
--- a/docs/self-hosting/govern/integrations/github.md
+++ b/docs/self-hosting/govern/integrations/github.md
@@ -4,7 +4,6 @@ description: Learn how to configure github for plane integration for self-hosted
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure GitHub for Plane integration
This guide walks you through setting up a GitHub App to enable GitHub integration for your Plane workspace on a self-hosted instance. Since self-hosted environments don’t come pre-configured for GitHub, you’ll need to set up the necessary authentication, permissions, and webhooks to ensure smooth integration.
@@ -12,12 +11,13 @@ This guide walks you through setting up a GitHub App to enable GitHub integratio
This guide covers configuration for both:
- **[GitHub Cloud](/self-hosting/govern/integrations/github#github-cloud)**
-The standard cloud-hosted GitHub service
+ The standard cloud-hosted GitHub service
- **[GitHub Enterprise Server](/self-hosting/govern/integrations/github#github-enterprise-server)**
-Self-hosted GitHub instances for organizations with specific compliance or security requirements
+ Self-hosted GitHub instances for organizations with specific compliance or security requirements
In this guide, you’ll:
+
1. [Create and configure a GitHub App](/self-hosting/govern/integrations/github#create-github-app)
2. [Set up permissions and events](/self-hosting/govern/integrations/github#set-up-permissions-and-events)
3. [Configure your Plane instance](/self-hosting/govern/integrations/github#configure-plane-instance)
@@ -25,7 +25,7 @@ In this guide, you’ll:
::: warning
**Activate GitHub integration**
-After creating and configuring the GitHub app and configuring the instance as detailed on this page, you'll need to [setup the GitHub integration](https://docs.plane.so/integrations/github) within Plane.
+After creating and configuring the GitHub app and configuring the instance as detailed on this page, you'll need to [setup the GitHub integration](https://docs.plane.so/integrations/github) within Plane.
:::
## Create GitHub App
@@ -107,6 +107,7 @@ To configure GitHub integration, you'll need to create a GitHub App within your
5. In the **Post installation** section, add the below **Setup URL**.
**For Plane cloud instance**
+
```bash
https://silo.plane.so/api/oauth/github-enterprise/auth/callback
```
@@ -116,6 +117,7 @@ To configure GitHub integration, you'll need to create a GitHub App within your
```bash
https:///silo/api/oauth/github-enterprise/auth/callback
```
+
Redirects users to this URL after GitHub app installation.

@@ -124,6 +126,7 @@ To configure GitHub integration, you'll need to create a GitHub App within your
7. In the **Webhook** section, add the below **Webhook URL**.
**For Plane cloud instance**
+
```bash
https://silo.plane.so/api/github-enterprise/github-webhook
```
@@ -133,50 +136,49 @@ To configure GitHub integration, you'll need to create a GitHub App within your
```bash
https:///silo/api/github-enterprise/github-webhook
```
+
This allows Plane to receive updates from GitHub repositories.

:::
-
### Set up permissions and events
1. Add repository and account permissions by setting the **Access** dropdown next to each permission, as shown in the tables below.
- 
+ 
- **Repository permissions**
+ **Repository permissions**
- |Permission |Access level |Purpose|
- |---------|---------------------|-----------|
- |Issues|Read and write|Enables reading, creating, updating, closing, and commenting on issues within the repository.|
- |Metadata|Read-only|Provides read-only access to repository metadata, such as its name, description, and visibility.|
- |Pull requests|Read and write|Allows reading, creating, updating, merging, and commenting on pull requests.|
+ | Permission | Access level | Purpose |
+ | ---------------------------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------ |
+ | Issues | Read and write | Enables reading, creating, updating, closing, and commenting on issues within the repository. |
+ | Metadata | Read-only | Provides read-only access to repository metadata, such as its name, description, and visibility. |
+ | Pull requests | Read and write | Allows reading, creating, updating, merging, and commenting on pull requests. |
- **Account permissions**
+ **Account permissions**
- |Permission |Access level |Purpose|
- |---------|------------|-----------|
- |Email addresses|Read-only|Grants access to users' email addresses, typically for notifications or communication.|
- |Profile|Read and write|Enables access to user profile details like name, username, and avatar.|
+ | Permission | Access level | Purpose |
+ | ---------------------------------------------------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------- |
+ | Email addresses | Read-only | Grants access to users' email addresses, typically for notifications or communication. |
+ | Profile | Read and write | Enables access to user profile details like name, username, and avatar. |
+2. In the **Subscribe to events** section, turn on all the required events below.
-2. In the **Subscribe to events** section, turn on all the required events below.
+ 
- 
-
- |Event |Purpose|
- |---------|------------|
- |Installation target| This is where the repositories or organizations where your GitHub App is installed. This determines which repositories Plane can sync with.|
- |Meta|Includes metadata about the app's configuration and setup. This is essential for maintaining integration stability.|
- |Issue comment| Triggers when a comment is added, edited, or deleted on an issue. Useful for keeping comments synced between Plane and GitHub.|
- |Issues|Triggers when an issue is created, updated, closed, reopened, assigned, labeled, or transferred. Ensures issue status and details remain consistent between Plane and GitHub.|
- |Pull request|Fires when a pull request is opened, closed, merged, edited, or labeled. Essential for tracking development progress.|
- |Pull request review|Activates when a review is submitted, edited, or dismissed. Keeps review activities aligned between Plane and GitHub.|
- |Pull request review comment|Fires when a review comment is added, modified, or removed. Ensures feedback is reflected across both platforms.|
- |Pull request review thread|Triggers when a review discussion thread is resolved or reopened. Helps maintain visibility on code review discussions.|
- |Push|Activates when new commits are pushed to a repository. Useful for tracking code updates and changes.|
- |Repository sub issues|Tracks issues within a repository that are linked to or managed by another issue. Ensures accurate synchronization of related issues.|
+ | Event | Purpose |
+ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+ | Installation target | This is where the repositories or organizations where your GitHub App is installed. This determines which repositories Plane can sync with. |
+ | Meta | Includes metadata about the app's configuration and setup. This is essential for maintaining integration stability. |
+ | Issue comment | Triggers when a comment is added, edited, or deleted on an issue. Useful for keeping comments synced between Plane and GitHub. |
+ | Issues | Triggers when an issue is created, updated, closed, reopened, assigned, labeled, or transferred. Ensures issue status and details remain consistent between Plane and GitHub. |
+ | Pull request | Fires when a pull request is opened, closed, merged, edited, or labeled. Essential for tracking development progress. |
+ | Pull request review | Activates when a review is submitted, edited, or dismissed. Keeps review activities aligned between Plane and GitHub. |
+ | Pull request review comment | Fires when a review comment is added, modified, or removed. Ensures feedback is reflected across both platforms. |
+ | Pull request review thread | Triggers when a review discussion thread is resolved or reopened. Helps maintain visibility on code review discussions. |
+ | Push | Activates when new commits are pushed to a repository. Useful for tracking code updates and changes. |
+ | Repository sub issues | Tracks issues within a repository that are linked to or managed by another issue. Ensures accurate synchronization of related issues. |
3. Click the **Create GitHub App** button at the bottom of the page.
@@ -251,6 +253,7 @@ To configure GitHub integration, you'll need to create a GitHub App within your
- Private key
7. Convert the Private key to convert it to base64. Since private keys are typically multi-line, they can cause parsing errors or issues when setting environment variables. To avoid this, run the following command to convert the key to base64:
+
```bash
cat private_key.pem | base64 -w 0
```
@@ -259,7 +262,6 @@ To configure GitHub integration, you'll need to create a GitHub App within your
:::
-
## Troubleshooting
### Invalid private key
@@ -271,14 +273,18 @@ To configure GitHub integration, you'll need to create a GitHub App within your
This error usually occurs when the private key is not correctly generated. To fix this, follow the below steps.
1. Generate a new private key.
-2. Convert the private key to base64.
- ```bash
- cat private_key.pem | base64 -w 0
- ```
+2. Convert the private key to base64.
+
+```bash
+ cat private_key.pem | base64 -w 0
+```
+
3. Add the private key to the `.env` file.
- ```bash
- GITHUB_PRIVATE_KEY=
- ```
+
+```bash
+ GITHUB_PRIVATE_KEY=
+```
+
4. Save the file and restart the instance.
### Unable to connect GitHub organization account or personal account
@@ -300,8 +306,8 @@ This error usually occurs when the callback URL is not correctly configured or t
This error usually occurs when the application secret is not correctly configured. To fix this, follow the below steps.
-1. Delete the `plane_app_details_github` key from redis cache. ```del plane_app_details_github```.
-2. Set the `SILO_BASE_URL` in env with plane self hosted url and restart the api server. ```export SILO_BASE_URL=https://```
+1. Delete the `plane_app_details_github` key from redis cache. `del plane_app_details_github`.
+2. Set the `SILO_BASE_URL` in env with plane self hosted url and restart the api server. `export SILO_BASE_URL=https://`
3. Run this command in api server shell `python manage.py reset_marketplace_app_secrets` to reset the application secrets.
4. Try to connect again to the organization account to Plane.
@@ -309,4 +315,4 @@ This error usually occurs when the application secret is not correctly configure
This error usually occurs when the GitHub integration is not correctly configured. To fix this, follow the below steps.
-1. Make sure you've `opted out` of Server Token expiration and reconnect once again to the organization account to Plane. Check in Github App Settings > Optional Features
\ No newline at end of file
+1. Make sure you've `opted out` of Server Token expiration and reconnect once again to the organization account to Plane. Check in Github App Settings > Optional Features
diff --git a/docs/self-hosting/govern/integrations/gitlab.md b/docs/self-hosting/govern/integrations/gitlab.md
index 190cd433..a5e52a03 100644
--- a/docs/self-hosting/govern/integrations/gitlab.md
+++ b/docs/self-hosting/govern/integrations/gitlab.md
@@ -4,7 +4,6 @@ description: Learn how to configure gitlab for plane integration for self-hosted
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure GitLab for Plane integration
This guide walks you through setting up a GitLab application to enable GitLab integration for your Plane workspace on a self-hosted instance. Since self-hosted environments don’t come pre-configured for GitLab, you’ll need to create an application, configure authentication, and set the necessary permissions to ensure seamless integration.
@@ -12,10 +11,10 @@ This guide walks you through setting up a GitLab application to enable GitLab in
This guide covers configuration for both:
- **[GitLab.com](/self-hosting/govern/integrations/gitlab#gitlab-cloud)**
-The standard cloud-hosted GitLab service
+ The standard cloud-hosted GitLab service
- **[GitLab Self-managed](/self-hosting/govern/integrations/gitlab#gitlab-self-managed)**
-Self-hosted GitLab instances for organizations with specific compliance or security requirements
+ Self-hosted GitLab instances for organizations with specific compliance or security requirements
In this guide, you’ll:
@@ -25,7 +24,7 @@ In this guide, you’ll:
::: warning
**Activate GitLab integration**
-After creating and configuring the GitLab application and configuring the instance as detailed on this page, you'll need to [setup the GitLab integration](https://docs.plane.so/integrations/gitlab) within Plane.
+After creating and configuring the GitLab application and configuring the instance as detailed on this page, you'll need to [setup the GitLab integration](https://docs.plane.so/integrations/gitlab) within Plane.
:::
## Create GitLab Application
@@ -41,28 +40,28 @@ After creating and configuring the GitLab application and configuring the instan
3. Navigate to the **Applications** tab.
4. Click on **Add new application** to begin the setup.
- 
+ 
5. Provide a **Name** for your application.
6. Enter the following **Redirect URI**, replacing [YOUR_DOMAIN] with your actual domain:
- ```bash
- https://[YOUR_DOMAIN]/silo/api/gitlab/auth/callback
- ```
+ ```bash
+ https://[YOUR_DOMAIN]/silo/api/gitlab/auth/callback
+ ```
7. Check the **Confidential** box.
- 
+ 
8. Set permissions by selecting the required **Scopes**. The table below explains each scope:
- |Permission|Explanation|
- |----------|-----------|
- |`api`|Grants full read/write access to the API, including all groups, projects, container registry, dependency proxy, and package registry. Required for API requests.|
- |`read_api`|Allows read-only access to all groups, projects, container registry, and package registry.|
- |`read_user`|Grants read-only access to user profiles via the /user API endpoint, including username, public email, and full name. Also provides access to /users endpoints.|
- |`read_repository`|Enables read-only access to repositories in private projects via Git-over-HTTP or the Repository Files API.|
- |`profile`|Grants read-only access to the user's profile data using OpenID Connect.|
- |`email`|Provides read-only access to the user's primary email address using OpenID Connect.|
+ | Permission | Explanation |
+ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+ | `api` | Grants full read/write access to the API, including all groups, projects, container registry, dependency proxy, and package registry. Required for API requests. |
+ | `read_api` | Allows read-only access to all groups, projects, container registry, and package registry. |
+ | `read_user` | Grants read-only access to user profiles via the /user API endpoint, including username, public email, and full name. Also provides access to /users endpoints. |
+ | `read_repository` | Enables read-only access to repositories in private projects via Git-over-HTTP or the Repository Files API. |
+ | `profile` | Grants read-only access to the user's profile data using OpenID Connect. |
+ | `email` | Provides read-only access to the user's primary email address using OpenID Connect. |
9. Click **Save Application** to finalize the setup.
@@ -77,28 +76,25 @@ After creating and configuring the GitLab application and configuring the instan
Fill in the application details with the following configuration:
- **Name**
-Enter a descriptive name for your application (e.g., `Plane Local Dev` or `Plane Integration`).
+ Enter a descriptive name for your application (e.g., `Plane Local Dev` or `Plane Integration`).
- **Redirect URI**
-The redirect URI depends on your Plane deployment:
+ The redirect URI depends on your Plane deployment:
- **For Plane Cloud:**
- ```
- https://silo.plane.so/api/oauth/gitlab-enterprise/auth/callback
- ```
+ **For Plane Cloud:**
+ ` https://silo.plane.so/api/oauth/gitlab-enterprise/auth/callback`
**For Plane Self-Hosted:**
- ```
- https:///silo/api/oauth/gitlab-enterprise/auth/callback
- ```
+` https:///silo/api/oauth/gitlab-enterprise/auth/callback
+ `
Replace `` with your actual Plane instance domain.
- **Confidential**
-Keep the **Confidential** checkbox enabled. This ensures the application uses a client secret for secure authentication.
+ Keep the **Confidential** checkbox enabled. This ensures the application uses a client secret for secure authentication.
- **Scopes**
-Select the following scopes to grant Plane the necessary permissions:
+ Select the following scopes to grant Plane the necessary permissions:
- **api** - Grants complete read/write access to the API, including all groups and projects
- **read_api** - Grants read access to the API, including all groups and projects
@@ -111,7 +107,6 @@ Select the following scopes to grant Plane the necessary permissions:
:::
-
## Configure Plane instance
:::tabs key:gitlab-edition
@@ -119,14 +114,15 @@ Select the following scopes to grant Plane the necessary permissions:
== GitLab Cloud {#gitlab-cloud}
1. Copy the **Application ID** and **Secret** from the newly created application.
- 
+ 
2. Add these environment variables with the values to your Plane instance's `.env` file.
- ```bash
- GITLAB_CLIENT_ID=
- GITLAB_CLIENT_SECRET=
- ```
+ ```bash
+ GITLAB_CLIENT_ID=
+ GITLAB_CLIENT_SECRET=
+ ```
+
3. Save the file and restart the instance.
4. Once you've completed the instance configuration, [activate the GitLab integration in Plane](https://docs.plane.so/integrations/gitlab?edition=gitlab-cloud).
@@ -134,9 +130,8 @@ Select the following scopes to grant Plane the necessary permissions:
== GitLab Self-managed {#gitlab-self-managed}
1. Copy the **Application ID** and **Secret** from the newly created application.
- 
+ 
2. Once you've created the application, [activate the GitLab Self-managed integration in Plane](https://docs.plane.so/integrations/gitlab?edition=gitlab-self-managed).
:::
-
diff --git a/docs/self-hosting/govern/integrations/sentry.md b/docs/self-hosting/govern/integrations/sentry.md
index ebe94100..4cfbf6ae 100644
--- a/docs/self-hosting/govern/integrations/sentry.md
+++ b/docs/self-hosting/govern/integrations/sentry.md
@@ -4,21 +4,22 @@ description: Learn how to configure sentry for plane integration for self-hosted
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure Sentry for Plane integration
This guide shows you how to set up Sentry integration for your self-hosted Plane instance. Unlike Plane Cloud where Sentry comes pre-configured, self-hosted instances require you to create a custom integration in Sentry and configure your Plane deployment with the necessary credentials.
::: info
**What you'll accomplish:**
+
1. Create a Sentry custom integration with proper permissions and webhooks
2. Configure your Plane instance with Sentry credentials
3. Enable error tracking and automatic issue creation from Sentry alerts
-:::
+ :::
## Before you begin
You'll need:
+
- Administrator access to your Sentry organization
- Access to your Plane instance configuration files
- Your Plane instance domain (e.g., `plane.yourcompany.com`)
@@ -33,14 +34,14 @@ A custom integration (also called a public integration) connects your Sentry org
4. Select **Public Integration**.
5. Fill in these fields on the integration creation screen:
- | Field | Value |
- |-------|-------|
- | **Name** | `Plane` (or any name you prefer) |
- | **Author** | Your organization name |
- | **Webhook URL** | `https://[YOUR_DOMAIN]/silo/api/sentry/sentry-webhook/` |
- | **Redirect URL** | `https://[YOUR_DOMAIN]/silo/api/oauth/sentry/auth/callback` |
- | **Verify Installation** | Disabled (recommended) |
- | **Alert Rule Action** | Enabled |
+ | Field | Value |
+ | ----------------------- | ----------------------------------------------------------- |
+ | **Name** | `Plane` (or any name you prefer) |
+ | **Author** | Your organization name |
+ | **Webhook URL** | `https://[YOUR_DOMAIN]/silo/api/sentry/sentry-webhook/` |
+ | **Redirect URL** | `https://[YOUR_DOMAIN]/silo/api/oauth/sentry/auth/callback` |
+ | **Verify Installation** | Disabled (recommended) |
+ | **Alert Rule Action** | Enabled |
::: tip
Replace `[YOUR_DOMAIN]` with your actual Plane instance domain. For example, if your Plane instance is at `plane.company.com`, your Webhook URL would be `https://plane.company.com/silo/api/sentry/sentry-webhook/`
@@ -64,6 +65,7 @@ Enables automatic Plane work item creation when Sentry alert rules trigger.
The schema defines how Sentry and Plane interact—what fields appear when creating issues and how alert rules behave.
Paste this schema into the **Schema** field:
+
```json
{
"elements": [
@@ -230,16 +232,16 @@ The second element enables automatic work item creation when Sentry alerts fire.
Configure these permissions to allow Sentry to interact with Plane appropriately:
-| Permission | Access Level | Why This Matters |
-|------------|--------------|------------------|
-| **Project** | Read | Access project details, tags, and debug files from Sentry |
-| **Team** | Read | Retrieve team member lists for assignee dropdowns |
-| **Release** | No Access | Not required for Plane integration |
-| **Distribution** | No Access | Not required for Plane integration |
-| **Issue & Event** | Read & Write | Create and link issues, sync status updates bidirectionally |
-| **Organization** | Read | Resolve organization IDs and retrieve repository information |
-| **Member** | Read | Access member details for assignee functionality |
-| **Alerts** | Read | Enable alert rule actions for automatic issue creation |
+| Permission | Access Level | Why This Matters |
+| ----------------- | ------------ | ------------------------------------------------------------ |
+| **Project** | Read | Access project details, tags, and debug files from Sentry |
+| **Team** | Read | Retrieve team member lists for assignee dropdowns |
+| **Release** | No Access | Not required for Plane integration |
+| **Distribution** | No Access | Not required for Plane integration |
+| **Issue & Event** | Read & Write | Create and link issues, sync status updates bidirectionally |
+| **Organization** | Read | Resolve organization IDs and retrieve repository information |
+| **Member** | Read | Access member details for assignee functionality |
+| **Alerts** | Read | Enable alert rule actions for automatic issue creation |
@@ -249,13 +251,13 @@ Webhooks keep Plane and Sentry synchronized. When issues change in Sentry, Plane
Enable the **issue** webhook with these events:
-| Event | Why It's Needed |
-|-------|-----------------|
-| **created** | Notify Plane when new Sentry issues are detected |
-| **resolved** | Update linked Plane work items when Sentry issues are resolved |
-| **assigned** | Sync assignee changes from Sentry to Plane |
-| **archived** | Reflect archived status in Plane |
-| **unresolved** | Update Plane when resolved issues reopen |
+| Event | Why It's Needed |
+| -------------- | -------------------------------------------------------------- |
+| **created** | Notify Plane when new Sentry issues are detected |
+| **resolved** | Update linked Plane work items when Sentry issues are resolved |
+| **assigned** | Sync assignee changes from Sentry to Plane |
+| **archived** | Reflect archived status in Plane |
+| **unresolved** | Update Plane when resolved issues reopen |
@@ -267,7 +269,7 @@ After saving your integration, Sentry generates OAuth credentials:
- **Client Secret** - A private key used to authenticate API requests
::: warning
-**Important**
+**Important**
The Client Secret is only displayed once immediately after creating the integration. Copy it now and store it securely. If you lose it, you'll need to regenerate the integration.
:::
@@ -280,9 +282,11 @@ Add Sentry credentials to your Plane instance so it can communicate with Sentry'
### Locate your configuration file
**For Docker deployments:**
+
- Edit `plane.env` in your Plane installation directory
**For Kubernetes deployments:**
+
- Edit your `custom-values.yaml` file or ConfigMap containing environment variables
### Add environment variables
@@ -290,6 +294,7 @@ Add Sentry credentials to your Plane instance so it can communicate with Sentry'
Add these variables to your Plane configuration:
**For Docker (`plane.env`):**
+
```bash
# Sentry Integration
SENTRY_BASE_URL=https://sentry.io
@@ -299,6 +304,7 @@ SENTRY_INTEGRATION_SLUG=plane
```
**For Kubernetes (`custom-values.yaml`):**
+
```yaml
env:
silo_envs:
@@ -312,12 +318,12 @@ Replace `` and `` with the credentials from
### Environment variable reference
-| Variable | Required | Default | Description |
-|----------|----------|---------|-------------|
-| `SENTRY_BASE_URL` | No | `https://sentry.io` | Base URL of your Sentry instance. For self-hosted Sentry, use your Sentry domain (e.g., `https://sentry.company.com`) |
-| `SENTRY_CLIENT_ID` | Yes | - | Client ID from your Sentry custom integration |
-| `SENTRY_CLIENT_SECRET` | Yes | - | Client Secret from your Sentry custom integration (only shown once during creation) |
-| `SENTRY_INTEGRATION_SLUG` | No | - | The slug identifier for your integration. Find this in your integration's URL: `https://org.sentry.io/settings/developer-settings/plane-local` (here `plane-local` is the slug) |
+| Variable | Required | Default | Description |
+| ------------------------- | -------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `SENTRY_BASE_URL` | No | `https://sentry.io` | Base URL of your Sentry instance. For self-hosted Sentry, use your Sentry domain (e.g., `https://sentry.company.com`) |
+| `SENTRY_CLIENT_ID` | Yes | - | Client ID from your Sentry custom integration |
+| `SENTRY_CLIENT_SECRET` | Yes | - | Client Secret from your Sentry custom integration (only shown once during creation) |
+| `SENTRY_INTEGRATION_SLUG` | No | - | The slug identifier for your integration. Find this in your integration's URL: `https://org.sentry.io/settings/developer-settings/plane-local` (here `plane-local` is the slug) |
::: info
**Using self-hosted Sentry?**
@@ -329,12 +335,14 @@ If you're running your own Sentry instance, change `SENTRY_BASE_URL` to your Sen
Apply the configuration changes:
**For Docker:**
+
```bash
docker compose down
docker compose up -d
```
**For Kubernetes:**
+
```bash
helm upgrade plane-app plane-enterprise.tgz \
--namespace plane \
@@ -345,5 +353,4 @@ helm upgrade plane-app plane-enterprise.tgz \
Once you’ve completed the instance configuration, [activate the Sentry integration](https://docs.plane.so/integrations/sentry#set-up-sentry-integration) in Plane.
-
-For questions about Sentry integration, contact [support@plane.so](mailto:support@plane.so).
\ No newline at end of file
+For questions about Sentry integration, contact [support@plane.so](mailto:support@plane.so).
diff --git a/docs/self-hosting/govern/integrations/slack.md b/docs/self-hosting/govern/integrations/slack.md
index 294a8743..25fe2c50 100644
--- a/docs/self-hosting/govern/integrations/slack.md
+++ b/docs/self-hosting/govern/integrations/slack.md
@@ -4,19 +4,19 @@ description: Learn how to configure slack for plane integration for self-hosted
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure Slack for Plane integration
This guide walks you through setting up a Slack App to enable Slack integration for your Plane workspace on a self-hosted instance. Since self-hosted environments don’t come pre-configured for Slack, you’ll need to set up the necessary authentication, permissions, and event subscriptions to ensure seamless communication between Plane and Slack.
In this guide, you’ll:
+
1. [Create and configure a Slack App](/self-hosting/govern/integrations/slack#create-slack-app)
2. [Configure your Plane instance](/self-hosting/govern/integrations/slack#configure-plane-instance)
::: warning
**Activate Slack integration**
-After creating and configuring the Slack app and configuring the instance as detailed on this page, you'll need to [set up the Slack integration](https://docs.plane.so/integrations/slack) within Plane.
+After creating and configuring the Slack app and configuring the instance as detailed on this page, you'll need to [set up the Slack integration](https://docs.plane.so/integrations/slack) within Plane.
:::
## Create Slack App
@@ -26,16 +26,15 @@ To configure Slack integration, you'll need to create a Slack App within your or
1. Go to [Your Apps](https://api.slack.com/apps) on Slack.
2. Click **Create an App**.
- 
+ 
3. Choose **From a manifest**.
- 
+ 
4. Select the workspace where you want the app installed.
5. Remove the default manifest and paste the one below, making sure to update the placeholders with your actual values.
- 
-
+ 
:::tabs key:manifest-file
@@ -43,102 +42,89 @@ To configure Slack integration, you'll need to create a Slack App within your or
```json
{
- "display_information": {
- "name": "[YOUR_APP_NAME]",
- "description": "[YOUR_APP_DESCRIPTION]",
- "background_color": "#224dab"
+ "display_information": {
+ "name": "[YOUR_APP_NAME]",
+ "description": "[YOUR_APP_DESCRIPTION]",
+ "background_color": "#224dab"
+ },
+ "features": {
+ "bot_user": {
+ "display_name": "[YOUR_APP_NAME]",
+ "always_online": false
},
- "features": {
- "bot_user": {
- "display_name": "[YOUR_APP_NAME]",
- "always_online": false
- },
- "shortcuts": [
- {
- "name": "Create new issue",
- "type": "message",
- "callback_id": "issue_shortcut",
- "description": "Create a new issue in plane"
- },
- {
- "name": "Link Work Item",
- "type": "message",
- "callback_id": "link_work_item",
- "description": "Links thread with an existing work item"
- }
- ],
- "slash_commands": [
- {
- "command": "/plane",
- "url": "https://[YOUR_DOMAIN]silo/api/slack/command/",
- "description": "Create issue in Plane",
- "should_escape": false
- }
- ],
- "unfurl_domains": [
- "[YOUR_DOMAIN]"
- ]
+ "shortcuts": [
+ {
+ "name": "Create new issue",
+ "type": "message",
+ "callback_id": "issue_shortcut",
+ "description": "Create a new issue in plane"
+ },
+ {
+ "name": "Link Work Item",
+ "type": "message",
+ "callback_id": "link_work_item",
+ "description": "Links thread with an existing work item"
+ }
+ ],
+ "slash_commands": [
+ {
+ "command": "/plane",
+ "url": "https://[YOUR_DOMAIN]silo/api/slack/command/",
+ "description": "Create issue in Plane",
+ "should_escape": false
+ }
+ ],
+ "unfurl_domains": ["[YOUR_DOMAIN]"]
+ },
+ "oauth_config": {
+ "redirect_urls": [
+ "https://[YOUR_DOMAIN]silo/api/slack/team/auth/callback/",
+ "https://[YOUR_DOMAIN]silo/api/slack/user/auth/callback/"
+ ],
+ "scopes": {
+ "user": ["chat:write", "identify", "im:read", "im:write", "links:write", "links:read"],
+ "bot": [
+ "channels:join",
+ "channels:read",
+ "users:read",
+ "users:read.email",
+ "chat:write",
+ "chat:write.customize",
+ "channels:history",
+ "groups:history",
+ "mpim:history",
+ "im:history",
+ "links:read",
+ "links:write",
+ "groups:read",
+ "im:read",
+ "mpim:read",
+ "reactions:read",
+ "reactions:write",
+ "files:read",
+ "files:write",
+ "im:write",
+ "commands"
+ ]
+ }
+ },
+ "settings": {
+ "event_subscriptions": {
+ "request_url": "https://[YOUR_DOMAIN]silo/api/slack/events",
+ "bot_events": ["link_shared", "message.channels", "message.im"]
},
- "oauth_config": {
- "redirect_urls": [
- "https://[YOUR_DOMAIN]silo/api/slack/team/auth/callback/",
- "https://[YOUR_DOMAIN]silo/api/slack/user/auth/callback/"
- ],
- "scopes": {
- "user": [
- "chat:write",
- "identify",
- "im:read",
- "im:write",
- "links:write",
- "links:read"
- ],
- "bot": [
- "channels:join",
- "channels:read",
- "users:read",
- "users:read.email",
- "chat:write",
- "chat:write.customize",
- "channels:history",
- "groups:history",
- "mpim:history",
- "im:history",
- "links:read",
- "links:write",
- "groups:read",
- "im:read",
- "mpim:read",
- "reactions:read",
- "reactions:write",
- "files:read",
- "files:write",
- "im:write",
- "commands"
- ]
- }
+ "interactivity": {
+ "is_enabled": true,
+ "request_url": "https://[YOUR_DOMAIN]silo/api/slack/action/",
+ "message_menu_options_url": "https://[YOUR_DOMAIN]silo/api/slack/options/"
},
- "settings": {
- "event_subscriptions": {
- "request_url": "https://[YOUR_DOMAIN]silo/api/slack/events",
- "bot_events": [
- "link_shared",
- "message.channels",
- "message.im"
- ]
- },
- "interactivity": {
- "is_enabled": true,
- "request_url": "https://[YOUR_DOMAIN]silo/api/slack/action/",
- "message_menu_options_url": "https://[YOUR_DOMAIN]silo/api/slack/options/"
- },
- "org_deploy_enabled": false,
- "socket_mode_enabled": false,
- "token_rotation_enabled": true
- }
+ "org_deploy_enabled": false,
+ "socket_mode_enabled": false,
+ "token_rotation_enabled": true
+ }
}
```
-
+
== YAML {yaml}
```yaml
@@ -215,10 +201,11 @@ event_subscriptions:
socket_mode_enabled: false
token_rotation_enabled: true
```
+
:::
6. Review the permissions and click **Create**.
- 
+ 
### Manifest reference
@@ -226,83 +213,84 @@ The manifest file defines the configuration for integrating Plane with Slack. It
#### Features
-| Feature | Explanation |
-| --- | --- |
-| `bot_user` | Required to send thread messages while syncing issues or sending Plane notifications to Slack.|
-| `slack_commands` | A Slack command (`/plane`) allows users to create issues directly from Slack using a slash command. |
-| `shortcuts` | After activation, users can create issues from messages inside Slack.|
-| `unfurl_domain` | Specifies the domain where Plane is hosted. When an issue, cycle, or module link is pasted in Slack, it generates a preview of the entity. |
+| Feature | Explanation |
+| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
+| `bot_user` | Required to send thread messages while syncing issues or sending Plane notifications to Slack. |
+| `slack_commands` | A Slack command (`/plane`) allows users to create issues directly from Slack using a slash command. |
+| `shortcuts` | After activation, users can create issues from messages inside Slack. |
+| `unfurl_domain` | Specifies the domain where Plane is hosted. When an issue, cycle, or module link is pasted in Slack, it generates a preview of the entity. |
#### Variables
-|Variable|Explanation|
-|------------|--------|
-|`YOUR_DOMAIN`|The domain where Plane is hosted. This is required for sending webhook events and authentication callbacks.|
-|`YOUR_APP_NAME`|The name you want to give your Slack app. "Plane" is a good default option.|
-|`YOUR_APP_DESCRIPTION`|A short description of your Slack app’s purpose.|
+| Variable | Explanation |
+| ---------------------- | ----------------------------------------------------------------------------------------------------------- |
+| `YOUR_DOMAIN` | The domain where Plane is hosted. This is required for sending webhook events and authentication callbacks. |
+| `YOUR_APP_NAME` | The name you want to give your Slack app. "Plane" is a good default option. |
+| `YOUR_APP_DESCRIPTION` | A short description of your Slack app’s purpose. |
#### Event subscription
For thread sync and link unfurling to work, event subscriptions must be enabled. These events send relevant activity to Plane.
-| Bot event | Explanation |
-| --- | --- |
-| `link_shared` | When a link is shared in Slack and its hostname matches `unfurl_domain`, Plane receives the event and generates a preview of the entity. |
-| `message_channels` | When a message is posted in a channel, an event is triggered in Plane to support thread sync. |
-| `message_im` | When a direct message (DM) is posted, an event is triggered in Plane to support thread sync. |
+| Bot event | Explanation |
+| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
+| `link_shared` | When a link is shared in Slack and its hostname matches `unfurl_domain`, Plane receives the event and generates a preview of the entity. |
+| `message_channels` | When a message is posted in a channel, an event is triggered in Plane to support thread sync. |
+| `message_im` | When a direct message (DM) is posted, an event is triggered in Plane to support thread sync. |
#### User permissions
-| Permission | Explanation |
-| --- | --- |
-| `chat:write` | Allows the bot to send messages in channels and conversations it is a member of. |
-| `identify` | Allows the bot to verify its own identity and retrieve basic information. |
-| `im:read` | Enables the bot to view direct messages (DMs) where it has been added. |
-| `im:write` | Allows the bot to send direct messages (DMs) to users. |
-| `links:write` | Permits the bot to add, edit, and remove link unfurls. |
-| `links:read` | Allows the bot to view link unfurls and associated metadata. |
+| Permission | Explanation |
+| ------------- | -------------------------------------------------------------------------------- |
+| `chat:write` | Allows the bot to send messages in channels and conversations it is a member of. |
+| `identify` | Allows the bot to verify its own identity and retrieve basic information. |
+| `im:read` | Enables the bot to view direct messages (DMs) where it has been added. |
+| `im:write` | Allows the bot to send direct messages (DMs) to users. |
+| `links:write` | Permits the bot to add, edit, and remove link unfurls. |
+| `links:read` | Allows the bot to view link unfurls and associated metadata. |
#### Bot permissions
-| Permission | Explanation |
-| --- | --- |
-| `channels:join` | Allows the bot to join public channels. |
-| `channels:read` | Permits viewing public channel information and members. |
-| `users:read` | Allows viewing user information and presence status. |
-| `users:read.email` | Enables access to users' email addresses. |
-| `chat:write` | Allows sending messages in channels and conversations. |
+| Permission | Explanation |
+| ---------------------- | -------------------------------------------------------------------------- |
+| `channels:join` | Allows the bot to join public channels. |
+| `channels:read` | Permits viewing public channel information and members. |
+| `users:read` | Allows viewing user information and presence status. |
+| `users:read.email` | Enables access to users' email addresses. |
+| `chat:write` | Allows sending messages in channels and conversations. |
| `chat:write.customize` | Enables customization of the bot's name and profile when sending messages. |
-| `channels:history` | Allows viewing message history in public channels. |
-| `groups:history` | Permits viewing message history in private channels. |
-| `mpim:history` | Enables access to message history in multi-person direct messages. |
-| `im:history` | Allows viewing message history in direct messages. |
-| `links:read` | Permits viewing link unfurls and associated metadata. |
-| `links:write` | Allows adding, editing, and removing link unfurls. |
-| `groups:read` | Enables viewing private channel information and members. |
-| `im:read` | Allows viewing direct messages where the bot is added. |
-| `mpim:read` | Permits viewing multi-person direct messages. |
-| `reactions:read` | Enables viewing emoji reactions on messages. |
-| `reactions:write` | Allows adding and removing emoji reactions. |
-| `files:read` | Permits viewing and downloading files. |
-| `files:write` | Enables uploading, editing, and deleting files. |
-| `im:write` | Allows sending direct messages to users. |
-| `commands` | Enables the bot to add and respond to slash commands. |
+| `channels:history` | Allows viewing message history in public channels. |
+| `groups:history` | Permits viewing message history in private channels. |
+| `mpim:history` | Enables access to message history in multi-person direct messages. |
+| `im:history` | Allows viewing message history in direct messages. |
+| `links:read` | Permits viewing link unfurls and associated metadata. |
+| `links:write` | Allows adding, editing, and removing link unfurls. |
+| `groups:read` | Enables viewing private channel information and members. |
+| `im:read` | Allows viewing direct messages where the bot is added. |
+| `mpim:read` | Permits viewing multi-person direct messages. |
+| `reactions:read` | Enables viewing emoji reactions on messages. |
+| `reactions:write` | Allows adding and removing emoji reactions. |
+| `files:read` | Permits viewing and downloading files. |
+| `files:write` | Enables uploading, editing, and deleting files. |
+| `im:write` | Allows sending direct messages to users. |
+| `commands` | Enables the bot to add and respond to slash commands. |
## Configure Plane instance
-After creating your Slack app, follow these steps:
+
+After creating your Slack app, follow these steps:
1. Go to the **Event Subscriptions** tab.
2. Click **Retry** to verify your event subscription URL.
- 
+ 
3. Navigate to the **Basic Information** tab on Slack to find your `client_id` and `client_secret`.
4. Add these environment variables with the values to your Plane instance's `.env` file.
- ```bash
- SLACK_CLIENT_ID=
- SLACK_CLIENT_SECRET=
- ```
-5. Save the file and restart the instance.
+ ```bash
+ SLACK_CLIENT_ID=
+ SLACK_CLIENT_SECRET=
+ ```
+5. Save the file and restart the instance.
-6. Once you've completed the instance configuration, [activate the Slack integration in Plane](https://docs.plane.so/integrations/slack).
\ No newline at end of file
+6. Once you've completed the instance configuration, [activate the Slack integration in Plane](https://docs.plane.so/integrations/slack).
diff --git a/docs/self-hosting/govern/ldap.md b/docs/self-hosting/govern/ldap.md
index 7486f0fc..463107f6 100644
--- a/docs/self-hosting/govern/ldap.md
+++ b/docs/self-hosting/govern/ldap.md
@@ -4,7 +4,6 @@ description: Setup LDAP authentication for Plane. Configure Lightweight Director
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# LDAP authentication
LDAP (Lightweight Directory Access Protocol) authentication lets your team sign in to Plane using their existing corporate credentials. Instead of creating separate Plane passwords, users authenticate through your organization's directory service.
@@ -22,131 +21,139 @@ You'll need:
## Configure LDAP authentication
1. Sign in to your Plane instance in [God Mode](/self-hosting/govern/instance-admin).
- 
+ 
2. Select **Authentication** from the left pane.
-3. Click **Configure** next to **LDAP** at the bottom of the page.
+3. Click **Configure** next to **LDAP** at the bottom of the page.
4. Enter your LDAP server details.
- 
- - **Server URI (required)**
- This is the address of your LDAP server. Include the protocol and port number.
+ 
+ - **Server URI (required)**
+ This is the address of your LDAP server. Include the protocol and port number.
+
+ **Format:**
+ - For unencrypted connections: `ldap://hostname:389`
+ - For encrypted connections (recommended): `ldaps://hostname:636`
+
+ **Examples:**
+
+ ```
+ ldap://ldap.company.com:389
+ ldaps://ad.company.com:636
+ ldap://192.168.1.100:389
+ ```
+
+ - **Bind DN (required)**
+
+ This is the username of the service account that Plane will use to search your directory. Think of it as Plane's "read-only" account on your LDAP server.
- **Format:**
- - For unencrypted connections: `ldap://hostname:389`
- - For encrypted connections (recommended): `ldaps://hostname:636`
+ The format varies depending on your directory service:
- **Examples:**
- ```
- ldap://ldap.company.com:389
- ldaps://ad.company.com:636
- ldap://192.168.1.100:389
- ```
+ **Active Directory examples:**
- - **Bind DN (required)**
+ ```
+ cn=PlaneService,ou=Service Accounts,dc=company,dc=com
+ plane-svc@company.com
+ ```
- This is the username of the service account that Plane will use to search your directory. Think of it as Plane's "read-only" account on your LDAP server.
+ **OpenLDAP examples:**
- The format varies depending on your directory service:
+ ```
+ cn=admin,dc=example,dc=com
+ cn=readonly,ou=services,dc=example,dc=com
+ ```
- **Active Directory examples:**
- ```
- cn=PlaneService,ou=Service Accounts,dc=company,dc=com
- plane-svc@company.com
- ```
+ - **Bind Password (required)**
- **OpenLDAP examples:**
- ```
- cn=admin,dc=example,dc=com
- cn=readonly,ou=services,dc=example,dc=com
- ```
+ Enter the password for your service account (Bind DN). Plane encrypts and stores this securely in its database.
- - **Bind Password (required)**
+ - **User Search Base (required)**
- Enter the password for your service account (Bind DN). Plane encrypts and stores this securely in its database.
+ This defines where in your directory Plane should look for users. Think of it as the "starting folder" for user searches.
- - **User Search Base (required)**
+ Use the most specific path possible for better performance.
- This defines where in your directory Plane should look for users. Think of it as the "starting folder" for user searches.
+ **Examples:**
- Use the most specific path possible for better performance.
+ ```
+ ou=users,dc=example,dc=com
+ ou=employees,ou=people,dc=company,dc=com
+ cn=users,dc=company,dc=local
+ ```
- **Examples:**
- ```
- ou=users,dc=example,dc=com
- ou=employees,ou=people,dc=company,dc=com
- cn=users,dc=company,dc=local
- ```
+ - **User Search Filter (optional)**
- - **User Search Filter (optional)**
+ This tells Plane how to find users when they try to sign in. Use `{username}` as a placeholder - Plane replaces it with whatever the user types in the login field.
- This tells Plane how to find users when they try to sign in. Use `{username}` as a placeholder - Plane replaces it with whatever the user types in the login field.
+ **Common filters by directory type:**
- **Common filters by directory type:**
+ | Directory Type | Filter | What it does |
+ | ---------------- | -------------------------------- | -------------------------------- |
+ | OpenLDAP | `(uid={username})` | Searches by user ID |
+ | Active Directory | `(sAMAccountName={username})` | Searches by Windows login name |
+ | Active Directory | `(userPrincipalName={username})` | Searches by email-style username |
+ | Any | `(mail={username})` | Searches by email address |
- | Directory Type | Filter | What it does |
- |----------------|--------|--------------|
- | OpenLDAP | `(uid={username})` | Searches by user ID |
- | Active Directory | `(sAMAccountName={username})` | Searches by Windows login name |
- | Active Directory | `(userPrincipalName={username})` | Searches by email-style username |
- | Any | `(mail={username})` | Searches by email address |
+ **Default:** If you don't specify a filter, Plane uses `(uid={username})`.
- **Default:** If you don't specify a filter, Plane uses `(uid={username})`.
+ **Combined filter example:**\
+ If you want users to sign in with either their username OR email:
- **Combined filter example:**\
- If you want users to sign in with either their username OR email:
- ```
- (|(uid={username})(mail={username}))
- ```
+ ```
+ (|(uid={username})(mail={username}))
+ ```
- - **User Attributes (optional)**
+ - **User Attributes (optional)**
- List the LDAP attributes Plane should retrieve to create user profiles. Plane uses these to populate the user's display name and email in Plane.
+ List the LDAP attributes Plane should retrieve to create user profiles. Plane uses these to populate the user's display name and email in Plane.
- **How Plane maps attributes:**
+ **How Plane maps attributes:**
- | Plane needs | LDAP provides (in order of preference) |
- |-------------|----------------------------------------|
- | Email address | `mail`, `userPrincipalName` |
- | First name | `givenName`, or first part of `cn` if `givenName` is missing |
- | Last name | `sn`, or last part of `cn` if `sn` is missing |
+ | Plane needs | LDAP provides (in order of preference) |
+ | ------------- | ------------------------------------------------------------ |
+ | Email address | `mail`, `userPrincipalName` |
+ | First name | `givenName`, or first part of `cn` if `givenName` is missing |
+ | Last name | `sn`, or last part of `cn` if `sn` is missing |
- **Recommended setting:**
- ```
- mail,cn,givenName,sn,userPrincipalName,displayName
- ```
+ **Recommended setting:**
- **Default:** If you don't specify attributes, Plane uses `mail,cn,givenName,sn`.
+ ```
+ mail,cn,givenName,sn,userPrincipalName,displayName
+ ```
- - **Provider Name (optional)**
+ **Default:** If you don't specify attributes, Plane uses `mail,cn,givenName,sn`.
- This is the label that appears on Plane's login button. Choose something your team will recognize.
+ - **Provider Name (optional)**
- **Examples:**
- - `Corporate Directory`
- - `Company SSO`
- - `Active Directory`
+ This is the label that appears on Plane's login button. Choose something your team will recognize.
- **Default:** If you don't specify a name, Plane shows `LDAP`.
+ **Examples:**
+ - `Corporate Directory`
+ - `Company SSO`
+ - `Active Directory`
- The login button will display as: **"Sign in with [Provider Name]"**
+ **Default:** If you don't specify a name, Plane shows `LDAP`.
+
+ The login button will display as: **"Sign in with [Provider Name]"**
5. Click **Save changes** to apply your LDAP settings. Plane will validate the connection to your LDAP server.
6. Users will see **Sign in with LDAP** on Plane's login page and can use their directory credentials to sign in.
- 
+ 
## How LDAP authentication works
LDAP authentication in Plane works through a two-phase process. First, Plane locates the user in your directory, then it verifies their credentials. This separation is fundamental to how LDAP works and explains why you need both a service account (Bind DN) and the user's own credentials.
### The service account pattern
+
Unlike simpler authentication systems where you might directly check a username and password against a database, LDAP uses what's called a "bind" operation. Plane needs to authenticate twice: once as itself (using the Bind DN) to search your directory, and once as the user to verify their password.
This is why you configure a Bind DN and password - it's Plane's identity on your LDAP server. Think of it as Plane introducing itself before asking about your users. The Bind DN only needs read access because Plane is just looking up information, never modifying your directory.
### The authentication flow
+
When a user tries to sign in, here's what happens behind the scenes:
-**Connection and service authentication**
+**Connection and service authentication**
Plane connects to your LDAP server using the Server URI, then authenticates using the Bind DN credentials. If this fails, no users can sign in; the service account must work first.
**User search**
@@ -162,6 +169,7 @@ Once authentication succeeds, Plane retrieves the User Attributes you configured
Finally, Plane creates a session for the user and redirects them into the workspace. From this point on, the user's session works identically to any other Plane session. The LDAP interaction is complete.
### Why search filters matter
+
The User Search Filter determines sign-in flexibility. A simple filter like `(uid={username})` requires exact usernames, but you can make it more flexible:
`(mail={username})` lets users sign in with email
@@ -170,6 +178,7 @@ The User Search Filter determines sign-in flexibility. A simple filter like `(ui
Filters can also restrict access: `(&(uid={username})(memberOf=cn=plane-users,ou=groups,dc=company,dc=com))` only permits specific group members.
### The role of user attributes
+
User Attributes tell Plane which LDAP fields to retrieve after authentication. This is separate from the search filter. The filter finds the user, the attributes populate their profile.
-Plane specifically looks for email addresses (required) and names (optional). If your LDAP server uses different attribute names, you need to include them.
\ No newline at end of file
+Plane specifically looks for email addresses (required) and names (optional). If your LDAP server uses different attribute names, you need to include them.
diff --git a/docs/self-hosting/govern/oidc-sso.md b/docs/self-hosting/govern/oidc-sso.md
index 1c2c6d63..26736ba5 100644
--- a/docs/self-hosting/govern/oidc-sso.md
+++ b/docs/self-hosting/govern/oidc-sso.md
@@ -4,7 +4,6 @@ description: Setup OIDC SSO authentication for Plane. Configure OpenID Connect s
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# OIDC SSO
Plane enables custom SSO via any identity provider with an official and supported implementation of OIDC standards. This page cites examples from Okta, but we will soon publish provider-specific instructions in phases.
@@ -18,21 +17,21 @@ You will need to configure values on your IdP first and then on Plane later.
Create a Plane client or application per your IdP's documentation and configure ↓.
::: tip
- `domain.tld` is the domain that you have hosted your Plane app on.
+`domain.tld` is the domain that you have hosted your Plane app on.
:::
-| **Config** | **Key** |
-|----------------|-------------------------------------------------------|
-| Origin URL | `http(s)://domain.tld/auth/oidc/` |
-| Callback URL | `http(s)://domain.tld/auth/oidc/callback/` |
-| Logout URL | `http(s)://domain.tld/auth/oidc/logout/` |
+| **Config** | **Key** |
+| ------------ | ------------------------------------------ |
+| Origin URL | `http(s)://domain.tld/auth/oidc/` |
+| Callback URL | `http(s)://domain.tld/auth/oidc/callback/` |
+| Logout URL | `http(s)://domain.tld/auth/oidc/logout/` |
### On Plane
Go to `/god-mode/authentication/oidc` on your Plane app and find the configs ↓.
::: tip
- Your IdP will generate some of the following configs for you. Others, you will specify yourself. Just copy them over to each field.
+Your IdP will generate some of the following configs for you. Others, you will specify yourself. Just copy them over to each field.
:::

@@ -59,6 +58,6 @@ Go to `/god-mode/authentication/oidc` on your Plane app and find the configs ↓
To test if this URL is right, see if clicking the `Login with ` button brings up your IdP's authentication screen.
- 
+ 
-- Finally, choose a name for your IdP on Plane so you can recognize this set of configs.
\ No newline at end of file
+- Finally, choose a name for your IdP on Plane so you can recognize this set of configs.
diff --git a/docs/self-hosting/govern/private-bucket.md b/docs/self-hosting/govern/private-bucket.md
index 035c2061..48ba9ff5 100644
--- a/docs/self-hosting/govern/private-bucket.md
+++ b/docs/self-hosting/govern/private-bucket.md
@@ -4,7 +4,6 @@ description: Learn how to switch from public to private buckets • commercial e
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Switch from public to private buckets • Commercial Edition
::: warning
@@ -30,6 +29,7 @@ Simply run the command ↓.
```bash
docker exec -it python manage.py update_bucket
```
+
A successful run keeps any public files you already have accessible while moving you to private storage.
## For external storage • S3 or S3 compatible
@@ -43,6 +43,7 @@ This step is critical if you are using external storage to ensure continued func
:::
Here’s a sample CORS policy for your reference. Just replace `` with your actual domain and apply the policy to your bucket.
+
```bash
[
{
@@ -78,51 +79,53 @@ Don't start from here if you haven't updated your CORS policy.
To migrate from public to private bucket storage, follow the instructions below:
-1. First, make sure you have the following permissions on your S3 bucket. If you don't, make changes to get those permissions on your bucket first.
- - **s3:GetObject**
- So you can access your public files so far To access existing objects publicly
+1. First, make sure you have the following permissions on your S3 bucket. If you don't, make changes to get those permissions on your bucket first.
+ - **s3:GetObject**
+ So you can access your public files so far To access existing objects publicly
- - **s3:ListBucket**
- So you can apply policies to your bucket for public access
+ - **s3:ListBucket**
+ So you can apply policies to your bucket for public access
- - **s3:PutObject**
- So you can create new files
+ - **s3:PutObject**
+ So you can create new files
- - **s3:PutBucketPolicy**
- So you can update your buckets' policy
+ - **s3:PutBucketPolicy**
+ So you can update your buckets' policy
2. Now, run the command ↓.
- ```bash
- docker exec -it python manage.py update_bucket
- ```
- ::: tip
- 1. If the command finds the necessary permissions missing, it will generate a `permissions.json` file which you can use to update your bucket policy manually. Here’s how the `permissions.json` file should look.
-
- ```bash
- {
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Principal": "*",
- "Action": "s3:GetObject",
- "Resource": [
- "arn:aws:s3:::/",
- "arn:aws:s3:::/"
- ]
- }
- ]
- }
- ```
-
- 2. To copy the `permissions.json` file to the local machine, run the command ↓.
-
- ```bash
- docker cp :/code/permissions.json .
- ```
-
- :::
+
+ ```bash
+ docker exec -it python manage.py update_bucket
+ ```
+
+ ::: tip
+ 1. If the command finds the necessary permissions missing, it will generate a `permissions.json` file which you can use to update your bucket policy manually. Here’s how the `permissions.json` file should look.
+
+ ```bash
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "s3:GetObject",
+ "Resource": [
+ "arn:aws:s3:::/",
+ "arn:aws:s3:::/"
+ ]
+ }
+ ]
+ }
+ ```
+
+ 2. To copy the `permissions.json` file to the local machine, run the command ↓.
+
+ ```bash
+ docker cp :/code/permissions.json .
+ ```
+
+ :::
## Troubleshoot
-- [Bucket policy exceeds size limit](/self-hosting/troubleshoot/storage-errors#bucket-policy-exceeds-size-limit)
\ No newline at end of file
+- [Bucket policy exceeds size limit](/self-hosting/troubleshoot/storage-errors#bucket-policy-exceeds-size-limit)
diff --git a/docs/self-hosting/govern/reset-password.md b/docs/self-hosting/govern/reset-password.md
index 8c7adde8..2a304f74 100644
--- a/docs/self-hosting/govern/reset-password.md
+++ b/docs/self-hosting/govern/reset-password.md
@@ -4,24 +4,26 @@ description: Learn how to reset password for self-hosted Plane. Complete guide w
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Reset password
+
Users can reset their password through the terminal of the Plane application. You need to login to backend docker container and run the below command for resetting a user’s password.
1. Get the container id for **plane-api**.
- ```bash
- docker ps
- ```
+
+ ```bash
+ docker ps
+ ```
2. Log in to the container.
- ```bash
- docker exec -it /bin/sh
- ```
+
+ ```bash
+ docker exec -it /bin/sh
+ ```
3. Run the reset password command.
- ```bash
- python manage.py reset_password
- ```
-::: tip
-The email should be of an already existing user on the Plane application. If the email is not attached to any user the command will throw an error.
-:::
\ No newline at end of file
+ `bash
+python manage.py reset_password
+`
+ ::: tip
+ The email should be of an already existing user on the Plane application. If the email is not attached to any user the command will throw an error.
+ :::
diff --git a/docs/self-hosting/govern/reverse-proxy.md b/docs/self-hosting/govern/reverse-proxy.md
index 3426fac7..e2c8c560 100644
--- a/docs/self-hosting/govern/reverse-proxy.md
+++ b/docs/self-hosting/govern/reverse-proxy.md
@@ -4,32 +4,34 @@ description: Learn how to configure external reverse proxy for self-hosted Plane
keywords: plane, self-hosting, deployment, plane installation, configuration, administration
---
-
# Configure external reverse proxy