deploy test
52
about/how-pangolin-works.mdx
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "How Pangolin Works"
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
Pangolin is an open-source system composed of several interconnected components that work together to provide secure, application-specific access. The central server, often referred to as the point of presence, is the core of the system. In self-hosted deployments, the point of presence typically includes Pangolin, Gerbil, and Traefik with its custom plugin, Badger.
|
||||
|
||||
A site represents a connection to an edge network. Pangolin can manage multiple edge networks simultaneously through its lightweight site client, Newt, which facilitates secure communication between the point of presence and the edge networks.
|
||||
|
||||
## System Diagram
|
||||
|
||||
<Card img="/images/system-diagram.svg">
|
||||
</Card>
|
||||
|
||||
## Components
|
||||
|
||||
### Pangolin
|
||||
|
||||
Pangolin serves as the main control plane and orchestrates the system. It includes:
|
||||
- An external-facing REST API for user interactions.
|
||||
- A WebSocket server for managing connections to Newt clients.
|
||||
- An internal REST API for communication between system components.
|
||||
- A frontend server for the web interface.
|
||||
- Integration with the main database for data storage.
|
||||
- A built-in authentication system for Zero Trust Network Access (ZTNA).
|
||||
|
||||
### Gerbil
|
||||
|
||||
Gerbil is responsible for managing WireGuard tunnels. It acts as a peer management server, creating and maintaining secure, encrypted tunnels between edge networks (sites) and the point of presence.
|
||||
|
||||
### Newt
|
||||
|
||||
Newt is a lightweight, user-space client designed to run on edge networks. It:
|
||||
|
||||
- Searches for the closest point of presence for optimal connectivity.
|
||||
- Connects to the Pangolin server via WebSocket and to Gerbil using a fully user-space WireGuard implementation.
|
||||
- Facilitates access to other resources on the edge network by creating TCP/UDP proxies.
|
||||
|
||||
### Traefik
|
||||
|
||||
Traefik is a high-performance, modular reverse proxy that routes requests to backend resources. It handles middleware, SSL termination, and provides extensibility through its plugin system. Key features include:
|
||||
|
||||
- Badger: A custom authentication middleware plugin.
|
||||
- Compatibility with security plugins like GeoBlock and CrowdSec for enhanced protection.
|
||||
|
||||
### Badger
|
||||
|
||||
Badger is Pangolin’s custom Traefik middleware plugin that enforces authentication. It:
|
||||
|
||||
- Intercepts incoming requests to the Traefik reverse proxy.
|
||||
- Redirects unauthenticated requests to the Pangolin server for authentication, ensuring secure access to backend resources.
|
||||
56
about/pangolin-vs-traditional-reverse-proxy.mdx
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: "Pangolin vs. Reverse Proxy"
|
||||
---
|
||||
|
||||
Pangolin shares many similarities with traditional reverse proxies, as it builds upon the same foundational principles. In fact, Pangolin leverages Traefik, one of the most popular reverse proxies, as its core component.
|
||||
|
||||
Like a traditional reverse proxy, Pangolin acts as an intermediary between clients and backend servers. Requests are routed through Pangolin, which determines the appropriate backend server to handle the request. This ensures that clients never directly communicate with backend servers. Pangolin also handles key reverse proxy functionalities, including:
|
||||
|
||||
- **Routing**: Directing traffic to the appropriate backend service.
|
||||
- **SSL Termination**: Managing HTTPS encryption and decryption.
|
||||
- **Logging**: Capturing and storing request/response data.
|
||||
- **Middleware Management**: Supporting plugins and middleware for additional functionality.
|
||||
|
||||
## Key Differences
|
||||
|
||||
### Tunneling
|
||||
|
||||
Traditional reverse proxies typically operate on the same network as the backend servers they connect to. This setup requires:
|
||||
|
||||
- A public IP address for the network.
|
||||
- Open ports (e.g., TCP 443 and 80) to allow external traffic.
|
||||
|
||||
Pangolin eliminates these requirements by introducing tunneling, which allows it to operate on a completely separate server and network from the backend services. Key benefits of Pangolin's tunneling include:
|
||||
|
||||
- **Centralized Management**: Multiple isolated edge networks can connect to a single Pangolin instance, meaning you only need to manage one reverse proxy server.
|
||||
- **Encrypted Traffic**: All traffic between the edge network and the central Pangolin server is fully encrypted.
|
||||
- **No Public IP or Open Ports**: Edge networks do not require a public IP address or open ports, reducing the attack surface and simplifying network configurations.
|
||||
|
||||
This tunneling capability makes Pangolin particularly useful for environments with restrictive network policies, such as those behind Carrier-Grade NAT (CGNAT) or firewalls.
|
||||
|
||||
### Identity-Aware Proxy (IAP)
|
||||
|
||||
Pangolin incorporates Identity-Aware Proxy (IAP) functionality, enabling zero-trust access to backend services. Unlike traditional reverse proxies, which often rely on network-based trust, Pangolin evaluates every access request based on user identity, device, location, and other contextual factors.
|
||||
|
||||
#### How IAP Works
|
||||
|
||||
1. **User Request**: A user attempts to access a protected internal web app, API, or resource.
|
||||
2. **Request Interception**: The request is intercepted by Pangolin's IAP instead of being routed directly to the backend.
|
||||
3. **Authentication & Authorization**: Pangolin verifies the user’s identity using OAuth2/OpenID (e.g., Google, Azure AD, Okta).
|
||||
4. **Context-Aware Checks**: Additional conditions, such as IP address, group membership, or geographic location, are evaluated.
|
||||
5. **Access Decision**: If all checks are passed, the request is forwarded to the backend service; otherwise, access is denied.
|
||||
|
||||
#### Access Control Features
|
||||
|
||||
Pangolin provides a robust suite of access control mechanisms, including but not limited to:
|
||||
|
||||
- **User and Role-Based Access Control (RBAC)**: Define granular permissions for users and roles.
|
||||
- **Resource-Specific Security**:
|
||||
- PIN codes and passwords for individual resources.
|
||||
- Shareable links with expiration dates.
|
||||
- **Authentication Options**:
|
||||
- Email-based One-Time Passwords (OTP).
|
||||
- Single Sign-On (SSO) with external identity providers via OIDC.
|
||||
- Two-Factor Authentication (2FA) and passkeys.
|
||||
- **Contextual Rules**:
|
||||
- IP, CIDR, and path-based access rules.
|
||||
28
about/pangolin-vs-vpn.mdx
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: "Pangolin vs. VPN"
|
||||
---
|
||||
|
||||
Pangolin and VPNs both provide secure remote access, but they differ in functionality and use cases. VPNs grant full network-level access, requiring client-side software to connect, while Pangolin provides application-specific access directly through a web browser with authentication, eliminating the need for additional software on the user’s device.
|
||||
|
||||
## Key Differences
|
||||
|
||||
### Access Scope
|
||||
|
||||
- **Pangolin**: Exposes specific applications or services securely. Users access resources via a browser, ensuring no full network access is granted.
|
||||
- **VPN**: Provides unrestricted access to the entire private network, which can increase security risks if a device is compromised.
|
||||
|
||||
### Access Control
|
||||
|
||||
- **Pangolin**: Enforces zero-trust policies with role-based access control (RBAC), path-based rules, and authentication methods like SSO, OIDC, and 2FA.
|
||||
- **VPN**: Relies on network segmentation or ACLs for security, with fewer granular controls.
|
||||
|
||||
### Deployment
|
||||
|
||||
- **Pangolin**: Operates as a centralized reverse proxy using encrypted WireGuard tunnels, requiring no public IPs or open ports on edge networks.
|
||||
- **VPN**: Requires a VPN server, public IPs, and open ports for inbound connections.
|
||||
|
||||
# Pangolin vs. Mesh VPN (e.g., Tailscale, Netbird)
|
||||
|
||||
Pangolin and mesh VPNs like Tailscale or Netbird both provide secure remote access, but they differ in their approach and functionality. Mesh VPNs focus on creating peer-to-peer connections between devices for full network access, while Pangolin is designed to expose specific applications or services securely through points of presence, with no need for client-side software on user devices.
|
||||
|
||||
Pangolin is a better choice for application-specific access with zero-trust security and no client-side software requirements. Mesh VPNs like Tailscale or Netbird are more suitable for full network access and peer-to-peer connectivity. For environments prioritizing granular access control and simplicity, Pangolin offers a more focused and secure solution.
|
||||
@@ -1,76 +0,0 @@
|
||||
---
|
||||
title: "Claude Code setup"
|
||||
description: "Configure Claude Code for your documentation workflow"
|
||||
icon: "asterisk"
|
||||
---
|
||||
|
||||
Claude Code is Anthropic's official CLI tool. This guide will help you set up Claude Code to help you write and maintain your documentation.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Active Claude subscription (Pro, Max, or API access)
|
||||
|
||||
## Setup
|
||||
|
||||
1. Install Claude Code globally:
|
||||
|
||||
```bash
|
||||
npm install -g @anthropic-ai/claude-code
|
||||
```
|
||||
|
||||
2. Navigate to your docs directory.
|
||||
3. (Optional) Add the `CLAUDE.md` file below to your project.
|
||||
4. Run `claude` to start.
|
||||
|
||||
## Create `CLAUDE.md`
|
||||
|
||||
Create a `CLAUDE.md` file at the root of your documentation repository to train Claude Code on your specific documentation standards:
|
||||
|
||||
````markdown
|
||||
# Mintlify documentation
|
||||
|
||||
## Working relationship
|
||||
- You can push back on ideas-this can lead to better documentation. Cite sources and explain your reasoning when you do so
|
||||
- ALWAYS ask for clarification rather than making assumptions
|
||||
- NEVER lie, guess, or make up information
|
||||
|
||||
## Project context
|
||||
- Format: MDX files with YAML frontmatter
|
||||
- Config: docs.json for navigation, theme, settings
|
||||
- Components: Mintlify components
|
||||
|
||||
## Content strategy
|
||||
- Document just enough for user success - not too much, not too little
|
||||
- Prioritize accuracy and usability of information
|
||||
- Make content evergreen when possible
|
||||
- Search for existing information before adding new content. Avoid duplication unless it is done for a strategic reason
|
||||
- Check existing patterns for consistency
|
||||
- Start by making the smallest reasonable changes
|
||||
|
||||
## Frontmatter requirements for pages
|
||||
- title: Clear, descriptive page title
|
||||
- description: Concise summary for SEO/navigation
|
||||
|
||||
## Writing standards
|
||||
- Second-person voice ("you")
|
||||
- Prerequisites at start of procedural content
|
||||
- Test all code examples before publishing
|
||||
- Match style and formatting of existing pages
|
||||
- Include both basic and advanced use cases
|
||||
- Language tags on all code blocks
|
||||
- Alt text on all images
|
||||
- Relative paths for internal links
|
||||
|
||||
## Git workflow
|
||||
- NEVER use --no-verify when committing
|
||||
- Ask how to handle uncommitted changes before starting
|
||||
- Create a new branch when no clear branch exists for changes
|
||||
- Commit frequently throughout development
|
||||
- NEVER skip or disable pre-commit hooks
|
||||
|
||||
## Do not
|
||||
- Skip frontmatter on any MDX file
|
||||
- Use absolute URLs for internal links
|
||||
- Include untested code examples
|
||||
- Make assumptions - always ask for clarification
|
||||
````
|
||||
@@ -1,420 +0,0 @@
|
||||
---
|
||||
title: "Cursor setup"
|
||||
description: "Configure Cursor for your documentation workflow"
|
||||
icon: "arrow-pointer"
|
||||
---
|
||||
|
||||
Use Cursor to help write and maintain your documentation. This guide shows how to configure Cursor for better results on technical writing tasks and using Mintlify components.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Cursor editor installed
|
||||
- Access to your documentation repository
|
||||
|
||||
## Project rules
|
||||
|
||||
Create project rules that all team members can use. In your documentation repository root:
|
||||
|
||||
```bash
|
||||
mkdir -p .cursor
|
||||
```
|
||||
|
||||
Create `.cursor/rules.md`:
|
||||
|
||||
````markdown
|
||||
# Mintlify technical writing rule
|
||||
|
||||
You are an AI writing assistant specialized in creating exceptional technical documentation using Mintlify components and following industry-leading technical writing practices.
|
||||
|
||||
## Core writing principles
|
||||
|
||||
### Language and style requirements
|
||||
|
||||
- Use clear, direct language appropriate for technical audiences
|
||||
- Write in second person ("you") for instructions and procedures
|
||||
- Use active voice over passive voice
|
||||
- Employ present tense for current states, future tense for outcomes
|
||||
- Avoid jargon unless necessary and define terms when first used
|
||||
- Maintain consistent terminology throughout all documentation
|
||||
- Keep sentences concise while providing necessary context
|
||||
- Use parallel structure in lists, headings, and procedures
|
||||
|
||||
### Content organization standards
|
||||
|
||||
- Lead with the most important information (inverted pyramid structure)
|
||||
- Use progressive disclosure: basic concepts before advanced ones
|
||||
- Break complex procedures into numbered steps
|
||||
- Include prerequisites and context before instructions
|
||||
- Provide expected outcomes for each major step
|
||||
- Use descriptive, keyword-rich headings for navigation and SEO
|
||||
- Group related information logically with clear section breaks
|
||||
|
||||
### User-centered approach
|
||||
|
||||
- Focus on user goals and outcomes rather than system features
|
||||
- Anticipate common questions and address them proactively
|
||||
- Include troubleshooting for likely failure points
|
||||
- Write for scannability with clear headings, lists, and white space
|
||||
- Include verification steps to confirm success
|
||||
|
||||
## Mintlify component reference
|
||||
|
||||
### Callout components
|
||||
|
||||
#### Note - Additional helpful information
|
||||
|
||||
<Note>
|
||||
Supplementary information that supports the main content without interrupting flow
|
||||
</Note>
|
||||
|
||||
#### Tip - Best practices and pro tips
|
||||
|
||||
<Tip>
|
||||
Expert advice, shortcuts, or best practices that enhance user success
|
||||
</Tip>
|
||||
|
||||
#### Warning - Important cautions
|
||||
|
||||
<Warning>
|
||||
Critical information about potential issues, breaking changes, or destructive actions
|
||||
</Warning>
|
||||
|
||||
#### Info - Neutral contextual information
|
||||
|
||||
<Info>
|
||||
Background information, context, or neutral announcements
|
||||
</Info>
|
||||
|
||||
#### Check - Success confirmations
|
||||
|
||||
<Check>
|
||||
Positive confirmations, successful completions, or achievement indicators
|
||||
</Check>
|
||||
|
||||
### Code components
|
||||
|
||||
#### Single code block
|
||||
|
||||
Example of a single code block:
|
||||
|
||||
```javascript config.js
|
||||
const apiConfig = {
|
||||
baseURL: 'https://api.example.com',
|
||||
timeout: 5000,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### Code group with multiple languages
|
||||
|
||||
Example of a code group:
|
||||
|
||||
<CodeGroup>
|
||||
```javascript Node.js
|
||||
const response = await fetch('/api/endpoint', {
|
||||
headers: { Authorization: `Bearer ${apiKey}` }
|
||||
});
|
||||
```
|
||||
|
||||
```python Python
|
||||
import requests
|
||||
response = requests.get('/api/endpoint',
|
||||
headers={'Authorization': f'Bearer {api_key}'})
|
||||
```
|
||||
|
||||
```curl cURL
|
||||
curl -X GET '/api/endpoint' \
|
||||
-H 'Authorization: Bearer YOUR_API_KEY'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
#### Request/response examples
|
||||
|
||||
Example of request/response documentation:
|
||||
|
||||
<RequestExample>
|
||||
```bash cURL
|
||||
curl -X POST 'https://api.example.com/users' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"name": "John Doe", "email": "john@example.com"}'
|
||||
```
|
||||
</RequestExample>
|
||||
|
||||
<ResponseExample>
|
||||
```json Success
|
||||
{
|
||||
"id": "user_123",
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"created_at": "2024-01-15T10:30:00Z"
|
||||
}
|
||||
```
|
||||
</ResponseExample>
|
||||
|
||||
### Structural components
|
||||
|
||||
#### Steps for procedures
|
||||
|
||||
Example of step-by-step instructions:
|
||||
|
||||
<Steps>
|
||||
<Step title="Install dependencies">
|
||||
Run `npm install` to install required packages.
|
||||
|
||||
<Check>
|
||||
Verify installation by running `npm list`.
|
||||
</Check>
|
||||
</Step>
|
||||
|
||||
<Step title="Configure environment">
|
||||
Create a `.env` file with your API credentials.
|
||||
|
||||
```bash
|
||||
API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Never commit API keys to version control.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
#### Tabs for alternative content
|
||||
|
||||
Example of tabbed content:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="macOS">
|
||||
```bash
|
||||
brew install node
|
||||
npm install -g package-name
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Windows">
|
||||
```powershell
|
||||
choco install nodejs
|
||||
npm install -g package-name
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Linux">
|
||||
```bash
|
||||
sudo apt install nodejs npm
|
||||
npm install -g package-name
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
#### Accordions for collapsible content
|
||||
|
||||
Example of accordion groups:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Troubleshooting connection issues">
|
||||
- **Firewall blocking**: Ensure ports 80 and 443 are open
|
||||
- **Proxy configuration**: Set HTTP_PROXY environment variable
|
||||
- **DNS resolution**: Try using 8.8.8.8 as DNS server
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Advanced configuration">
|
||||
```javascript
|
||||
const config = {
|
||||
performance: { cache: true, timeout: 30000 },
|
||||
security: { encryption: 'AES-256' }
|
||||
};
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Cards and columns for emphasizing information
|
||||
|
||||
Example of cards and card groups:
|
||||
|
||||
<Card title="Getting started guide" icon="rocket" href="/quickstart">
|
||||
Complete walkthrough from installation to your first API call in under 10 minutes.
|
||||
</Card>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Authentication" icon="key" href="/auth">
|
||||
Learn how to authenticate requests using API keys or JWT tokens.
|
||||
</Card>
|
||||
|
||||
<Card title="Rate limiting" icon="clock" href="/rate-limits">
|
||||
Understand rate limits and best practices for high-volume usage.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### API documentation components
|
||||
|
||||
#### Parameter fields
|
||||
|
||||
Example of parameter documentation:
|
||||
|
||||
<ParamField path="user_id" type="string" required>
|
||||
Unique identifier for the user. Must be a valid UUID v4 format.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="email" type="string" required>
|
||||
User's email address. Must be valid and unique within the system.
|
||||
</ParamField>
|
||||
|
||||
<ParamField query="limit" type="integer" default="10">
|
||||
Maximum number of results to return. Range: 1-100.
|
||||
</ParamField>
|
||||
|
||||
<ParamField header="Authorization" type="string" required>
|
||||
Bearer token for API authentication. Format: `Bearer YOUR_API_KEY`
|
||||
</ParamField>
|
||||
|
||||
#### Response fields
|
||||
|
||||
Example of response field documentation:
|
||||
|
||||
<ResponseField name="user_id" type="string" required>
|
||||
Unique identifier assigned to the newly created user.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="created_at" type="timestamp">
|
||||
ISO 8601 formatted timestamp of when the user was created.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="permissions" type="array">
|
||||
List of permission strings assigned to this user.
|
||||
</ResponseField>
|
||||
|
||||
#### Expandable nested fields
|
||||
|
||||
Example of nested field documentation:
|
||||
|
||||
<ResponseField name="user" type="object">
|
||||
Complete user object with all associated data.
|
||||
|
||||
<Expandable title="User properties">
|
||||
<ResponseField name="profile" type="object">
|
||||
User profile information including personal details.
|
||||
|
||||
<Expandable title="Profile details">
|
||||
<ResponseField name="first_name" type="string">
|
||||
User's first name as entered during registration.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="avatar_url" type="string | null">
|
||||
URL to user's profile picture. Returns null if no avatar is set.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Media and advanced components
|
||||
|
||||
#### Frames for images
|
||||
|
||||
Wrap all images in frames:
|
||||
|
||||
<Frame>
|
||||
<img src="/images/dashboard.png" alt="Main dashboard showing analytics overview" />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="The analytics dashboard provides real-time insights">
|
||||
<img src="/images/analytics.png" alt="Analytics dashboard with charts" />
|
||||
</Frame>
|
||||
|
||||
#### Videos
|
||||
|
||||
Use the HTML video element for self-hosted video content:
|
||||
|
||||
<video
|
||||
controls
|
||||
className="w-full aspect-video rounded-xl"
|
||||
src="link-to-your-video.com"
|
||||
></video>
|
||||
|
||||
Embed YouTube videos using iframe elements:
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl"
|
||||
src="https://www.youtube.com/embed/4KzFe50RQkQ"
|
||||
title="YouTube video player"
|
||||
frameBorder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
|
||||
#### Tooltips
|
||||
|
||||
Example of tooltip usage:
|
||||
|
||||
<Tooltip tip="Application Programming Interface - protocols for building software">
|
||||
API
|
||||
</Tooltip>
|
||||
|
||||
#### Updates
|
||||
|
||||
Use updates for changelogs:
|
||||
|
||||
<Update label="Version 2.1.0" description="Released March 15, 2024">
|
||||
## New features
|
||||
- Added bulk user import functionality
|
||||
- Improved error messages with actionable suggestions
|
||||
|
||||
## Bug fixes
|
||||
- Fixed pagination issue with large datasets
|
||||
- Resolved authentication timeout problems
|
||||
</Update>
|
||||
|
||||
## Required page structure
|
||||
|
||||
Every documentation page must begin with YAML frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "Clear, specific, keyword-rich title"
|
||||
description: "Concise description explaining page purpose and value"
|
||||
---
|
||||
```
|
||||
|
||||
## Content quality standards
|
||||
|
||||
### Code examples requirements
|
||||
|
||||
- Always include complete, runnable examples that users can copy and execute
|
||||
- Show proper error handling and edge case management
|
||||
- Use realistic data instead of placeholder values
|
||||
- Include expected outputs and results for verification
|
||||
- Test all code examples thoroughly before publishing
|
||||
- Specify language and include filename when relevant
|
||||
- Add explanatory comments for complex logic
|
||||
- Never include real API keys or secrets in code examples
|
||||
|
||||
### API documentation requirements
|
||||
|
||||
- Document all parameters including optional ones with clear descriptions
|
||||
- Show both success and error response examples with realistic data
|
||||
- Include rate limiting information with specific limits
|
||||
- Provide authentication examples showing proper format
|
||||
- Explain all HTTP status codes and error handling
|
||||
- Cover complete request/response cycles
|
||||
|
||||
### Accessibility requirements
|
||||
|
||||
- Include descriptive alt text for all images and diagrams
|
||||
- Use specific, actionable link text instead of "click here"
|
||||
- Ensure proper heading hierarchy starting with H2
|
||||
- Provide keyboard navigation considerations
|
||||
- Use sufficient color contrast in examples and visuals
|
||||
- Structure content for easy scanning with headers and lists
|
||||
|
||||
## Component selection logic
|
||||
|
||||
- Use **Steps** for procedures and sequential instructions
|
||||
- Use **Tabs** for platform-specific content or alternative approaches
|
||||
- Use **CodeGroup** when showing the same concept in multiple programming languages
|
||||
- Use **Accordions** for progressive disclosure of information
|
||||
- Use **RequestExample/ResponseExample** specifically for API endpoint documentation
|
||||
- Use **ParamField** for API parameters, **ResponseField** for API responses
|
||||
- Use **Expandable** for nested object properties or hierarchical information
|
||||
````
|
||||
@@ -1,96 +0,0 @@
|
||||
---
|
||||
title: "Windsurf setup"
|
||||
description: "Configure Windsurf for your documentation workflow"
|
||||
icon: "water"
|
||||
---
|
||||
|
||||
Configure Windsurf's Cascade AI assistant to help you write and maintain documentation. This guide shows how to set up Windsurf specifically for your Mintlify documentation workflow.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Windsurf editor installed
|
||||
- Access to your documentation repository
|
||||
|
||||
## Workspace rules
|
||||
|
||||
Create workspace rules that provide Windsurf with context about your documentation project and standards.
|
||||
|
||||
Create `.windsurf/rules.md` in your project root:
|
||||
|
||||
````markdown
|
||||
# Mintlify technical writing rule
|
||||
|
||||
## Project context
|
||||
|
||||
- This is a documentation project on the Mintlify platform
|
||||
- We use MDX files with YAML frontmatter
|
||||
- Navigation is configured in `docs.json`
|
||||
- We follow technical writing best practices
|
||||
|
||||
## Writing standards
|
||||
|
||||
- Use second person ("you") for instructions
|
||||
- Write in active voice and present tense
|
||||
- Start procedures with prerequisites
|
||||
- Include expected outcomes for major steps
|
||||
- Use descriptive, keyword-rich headings
|
||||
- Keep sentences concise but informative
|
||||
|
||||
## Required page structure
|
||||
|
||||
Every page must start with frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "Clear, specific title"
|
||||
description: "Concise description for SEO and navigation"
|
||||
---
|
||||
```
|
||||
|
||||
## Mintlify components
|
||||
|
||||
### Callouts
|
||||
|
||||
- `<Note>` for helpful supplementary information
|
||||
- `<Warning>` for important cautions and breaking changes
|
||||
- `<Tip>` for best practices and expert advice
|
||||
- `<Info>` for neutral contextual information
|
||||
- `<Check>` for success confirmations
|
||||
|
||||
### Code examples
|
||||
|
||||
- When appropriate, include complete, runnable examples
|
||||
- Use `<CodeGroup>` for multiple language examples
|
||||
- Specify language tags on all code blocks
|
||||
- Include realistic data, not placeholders
|
||||
- Use `<RequestExample>` and `<ResponseExample>` for API docs
|
||||
|
||||
### Procedures
|
||||
|
||||
- Use `<Steps>` component for sequential instructions
|
||||
- Include verification steps with `<Check>` components when relevant
|
||||
- Break complex procedures into smaller steps
|
||||
|
||||
### Content organization
|
||||
|
||||
- Use `<Tabs>` for platform-specific content
|
||||
- Use `<Accordion>` for progressive disclosure
|
||||
- Use `<Card>` and `<CardGroup>` for highlighting content
|
||||
- Wrap images in `<Frame>` components with descriptive alt text
|
||||
|
||||
## API documentation requirements
|
||||
|
||||
- Document all parameters with `<ParamField>`
|
||||
- Show response structure with `<ResponseField>`
|
||||
- Include both success and error examples
|
||||
- Use `<Expandable>` for nested object properties
|
||||
- Always include authentication examples
|
||||
|
||||
## Quality standards
|
||||
|
||||
- Test all code examples before publishing
|
||||
- Use relative paths for internal links
|
||||
- Include alt text for all images
|
||||
- Ensure proper heading hierarchy (start with h2)
|
||||
- Check existing patterns for consistency
|
||||
````
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
title: 'Create Plant'
|
||||
openapi: 'POST /plants'
|
||||
---
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
title: 'Delete Plant'
|
||||
openapi: 'DELETE /plants/{id}'
|
||||
---
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
title: 'Get Plants'
|
||||
openapi: 'GET /plants'
|
||||
---
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
title: 'New Plant'
|
||||
openapi: 'WEBHOOK /plant/webhook'
|
||||
---
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
title: 'Introduction'
|
||||
description: 'Example section for showcasing API endpoints'
|
||||
---
|
||||
|
||||
<Note>
|
||||
If you're not looking to build API reference documentation, you can delete
|
||||
this section by removing the api-reference folder.
|
||||
</Note>
|
||||
|
||||
## Welcome
|
||||
|
||||
There are two ways to build API documentation: [OpenAPI](https://mintlify.com/docs/api-playground/openapi/setup) and [MDX components](https://mintlify.com/docs/api-playground/mdx/configuration). For the starter kit, we are using the following OpenAPI specification.
|
||||
|
||||
<Card
|
||||
title="Plant Store Endpoints"
|
||||
icon="leaf"
|
||||
href="https://github.com/mintlify/starter/blob/main/api-reference/openapi.json"
|
||||
>
|
||||
View the OpenAPI specification file
|
||||
</Card>
|
||||
|
||||
## Authentication
|
||||
|
||||
All API endpoints are authenticated using Bearer tokens and picked up from the specification file.
|
||||
|
||||
```json
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -1,217 +0,0 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "OpenAPI Plant Store",
|
||||
"description": "A sample API that uses a plant store as an example to demonstrate features in the OpenAPI specification",
|
||||
"license": {
|
||||
"name": "MIT"
|
||||
},
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://sandbox.mintlify.com"
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/plants": {
|
||||
"get": {
|
||||
"description": "Returns all plants from the system that the user has access to",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"description": "The maximum number of results to return",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Plant response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Plant"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Creates a new plant in the store",
|
||||
"requestBody": {
|
||||
"description": "Plant to add to the store",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NewPlant"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "plant response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Plant"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/plants/{id}": {
|
||||
"delete": {
|
||||
"description": "Deletes a single plant based on the ID supplied",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"description": "ID of plant to delete",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Plant deleted",
|
||||
"content": {}
|
||||
},
|
||||
"400": {
|
||||
"description": "unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"webhooks": {
|
||||
"/plant/webhook": {
|
||||
"post": {
|
||||
"description": "Information about a new plant added to the store",
|
||||
"requestBody": {
|
||||
"description": "Plant added to the store",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NewPlant"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Return a 200 status to indicate that the data was received successfully"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Plant": {
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "The name of the plant",
|
||||
"type": "string"
|
||||
},
|
||||
"tag": {
|
||||
"description": "Tag to specify the type",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"NewPlant": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Plant"
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Identification number of the plant",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Error": {
|
||||
"required": [
|
||||
"error",
|
||||
"message"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes": {
|
||||
"bearerAuth": {
|
||||
"type": "http",
|
||||
"scheme": "bearer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
changelog.mdx
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: "Changelog"
|
||||
description: "Updates and announcements"
|
||||
---
|
||||
|
||||
<Update label="March 2025" description="v0.0.10">
|
||||
Added a new Wintergreen flavor.
|
||||
|
||||
Released a new version of the Spearmint flavor, now with 10% more mint.
|
||||
</Update>
|
||||
|
||||
<Update label="February 2025" description="v0.0.09">
|
||||
Released a new version of the Spearmint flavor.
|
||||
</Update>
|
||||
@@ -1,94 +0,0 @@
|
||||
---
|
||||
title: 'Development'
|
||||
description: 'Preview changes locally to update your docs'
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Prerequisites**:
|
||||
- Node.js version 19 or higher
|
||||
- A docs repository with a `docs.json` file
|
||||
</Info>
|
||||
|
||||
Follow these steps to install and run Mintlify on your operating system.
|
||||
|
||||
<Steps>
|
||||
<Step title="Install the Mintlify CLI">
|
||||
|
||||
```bash
|
||||
npm i -g mint
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Preview locally">
|
||||
|
||||
Navigate to your docs directory where your `docs.json` file is located, and run the following command:
|
||||
|
||||
```bash
|
||||
mint dev
|
||||
```
|
||||
|
||||
A local preview of your documentation will be available at `http://localhost:3000`.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Custom ports
|
||||
|
||||
By default, Mintlify uses port 3000. You can customize the port Mintlify runs on by using the `--port` flag. For example, to run Mintlify on port 3333, use this command:
|
||||
|
||||
```bash
|
||||
mint dev --port 3333
|
||||
```
|
||||
|
||||
If you attempt to run Mintlify on a port that's already in use, it will use the next available port:
|
||||
|
||||
```md
|
||||
Port 3000 is already in use. Trying 3001 instead.
|
||||
```
|
||||
|
||||
## Mintlify versions
|
||||
|
||||
Please note that each CLI release is associated with a specific version of Mintlify. If your local preview does not align with the production version, please update the CLI:
|
||||
|
||||
```bash
|
||||
npm mint update
|
||||
```
|
||||
|
||||
## Validating links
|
||||
|
||||
The CLI can assist with validating links in your documentation. To identify any broken links, use the following command:
|
||||
|
||||
```bash
|
||||
mint broken-links
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
If the deployment is successful, you should see the following:
|
||||
|
||||
<Frame>
|
||||
<img src="/images/checks-passed.png" alt="Screenshot of a deployment confirmation message that says All checks have passed." style={{ borderRadius: '0.5rem' }} />
|
||||
</Frame>
|
||||
|
||||
## Code formatting
|
||||
|
||||
We suggest using extensions on your IDE to recognize and format MDX. If you're a VSCode user, consider the [MDX VSCode extension](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx) for syntax highlighting, and [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) for code formatting.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title='Error: Could not load the "sharp" module using the darwin-arm64 runtime'>
|
||||
|
||||
This may be due to an outdated version of node. Try the following:
|
||||
1. Remove the currently-installed version of the CLI: `npm remove -g mint`
|
||||
2. Upgrade to Node v19 or higher.
|
||||
3. Reinstall the CLI: `npm i -g mint`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Issue: Encountering an unknown error">
|
||||
|
||||
Solution: Go to the root of your device and delete the `~/.mintlify` folder. Then run `mint dev` again.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
Curious about what changed in the latest CLI version? Check out the [CLI changelog](https://www.npmjs.com/package/mintlify?activeTab=versions).
|
||||
218
development/contributing.mdx
Normal file
@@ -0,0 +1,218 @@
|
||||
---
|
||||
title: "Development Guide"
|
||||
description: "Set up your local development environment for contributing to Pangolin"
|
||||
---
|
||||
|
||||
This guide describes how to set up your local development environment for contributing to Pangolin. We recommend using Docker Compose for the most consistent development experience across different environments.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Check>
|
||||
- Text Editor (VSCode, Neovim, etc.)
|
||||
- NodeJS v20.10.0
|
||||
- NPM v10.2.3 (or similar)
|
||||
- Go v1.23.1
|
||||
- Git
|
||||
- Docker & Docker Compose
|
||||
</Check>
|
||||
|
||||
<Info>
|
||||
For managing multiple versions of Go, you may want to use [gvm](https://github.com/moovweb/gvm).
|
||||
For managing multiple versions of NodeJS, you may want to use [nvm](https://github.com/nvm-sh/nvm).
|
||||
</Info>
|
||||
|
||||
## Setup Your Repository
|
||||
|
||||
Below is an example if you're working on the Pangolin repository.
|
||||
|
||||
<Steps>
|
||||
<Step title="Fork and clone">
|
||||
[Fork](https://help.github.com/articles/fork-a-repo/) the repository(ies) to your own GitHub account and [clone](https://help.github.com/articles/cloning-a-repository/) to your local device:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/pangolin.git
|
||||
cd pangolin/
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add upstream remote">
|
||||
Add the remote `upstream`:
|
||||
|
||||
```bash
|
||||
git remote add upstream https://github.com/fosrl/pangolin.git
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create feature branch">
|
||||
Create a new branch:
|
||||
|
||||
```bash
|
||||
git checkout -b BRANCH_NAME dev
|
||||
```
|
||||
|
||||
<Tip>
|
||||
It is recommended to give your branch a meaningful name, relevant to the feature or fix you are working on.
|
||||
|
||||
**Good examples**:
|
||||
- `docs-docker`
|
||||
- `feature-new-system`
|
||||
- `fix-title-cards`
|
||||
|
||||
**Bad examples**:
|
||||
- `bug`
|
||||
- `docs`
|
||||
- `feature`
|
||||
- `fix`
|
||||
- `patch`
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Open pull request">
|
||||
If you open a pull request, open it against the `dev` branch of the original repository.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Pangolin Development Setup
|
||||
|
||||
### Option 1: Docker Compose (Recommended)
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Advantages" icon="check">
|
||||
- Consistent environment
|
||||
- Easy setup and teardown
|
||||
- Isolated dependencies
|
||||
- Works on any OS
|
||||
</Card>
|
||||
|
||||
<Card title="Requirements" icon="docker">
|
||||
- Docker installed
|
||||
- Docker Compose installed
|
||||
- 4GB+ RAM available
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Steps>
|
||||
<Step title="Install dependencies">
|
||||
Install package dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure environment">
|
||||
Ensure you have a `config/` directory at the root with a `config.yml` inside. Refer to the [Pangolin Configuration docs](/self-host/advanced/config-file) or the `config.example.yml` in the repo for a sample of what to include in that file.
|
||||
|
||||
<Warning>
|
||||
You may need to tweak this to run in dev, such as setting the `dashboard_url` to `http://localhost:3002`.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Generate database schema">
|
||||
Generate the database schema and push it:
|
||||
|
||||
```bash
|
||||
npm run db:sqlite:generate
|
||||
npm run db:sqlite:push
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Start development server">
|
||||
Start the development server using Docker Compose:
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
<Tip>
|
||||
This will build and start all services in development mode with hot reloading enabled.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Note>
|
||||
When running Pangolin for the first time there will be no exit nodes. This means that there have been no Gerbil "exit nodes" registered in the database. When Gerbil first starts up and requests its config from Pangolin for the first time it gets registered as an exit node.
|
||||
|
||||
The easiest way to resolve this is to run Gerbil and have it register in your dev environment. Download the Gerbil binary and run it with localhost:
|
||||
|
||||
```bash
|
||||
./gerbil \
|
||||
--remoteConfig http://localhost:3001/api/v1/gerbil/get-config \
|
||||
--reportBandwidthTo http://localhost:3001/api/v1/gerbil/receive-bandwidth \
|
||||
--generateAndSaveKeyTo=/var/key \
|
||||
--reachableAt=http://localhost:3003
|
||||
```
|
||||
</Note>
|
||||
|
||||
### Option 2: Local Development
|
||||
|
||||
<Warning>
|
||||
Local development requires more setup and may have environment-specific issues. Docker Compose is recommended for consistency.
|
||||
</Warning>
|
||||
|
||||
<Steps>
|
||||
<Step title="Install dependencies">
|
||||
Install package dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure environment">
|
||||
Ensure you have a `config/` directory at the root with a `config.yml` inside. Refer to the [Pangolin Configuration docs](/self-host/advanced/config-file) or the `config.example.yml` in the repo for a sample of what to include in that file.
|
||||
|
||||
<Warning>
|
||||
You may need to tweak this to run in dev, such as setting the `dashboard_url` to `http://localhost:3002`.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Generate database schema">
|
||||
Generate the database schema and push it:
|
||||
|
||||
```bash
|
||||
npm run db:sqlite:generate
|
||||
npm run db:sqlite:push
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Start development server">
|
||||
Start the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Component Development
|
||||
|
||||
### Gerbil
|
||||
|
||||
<Card title="Requirements">
|
||||
- Go v1.23.1
|
||||
</Card>
|
||||
|
||||
```bash
|
||||
make local
|
||||
```
|
||||
|
||||
### Newt
|
||||
|
||||
<Card title="Requirements">
|
||||
- Go v1.23.1
|
||||
</Card>
|
||||
|
||||
```bash
|
||||
make local
|
||||
```
|
||||
|
||||
### Olm
|
||||
|
||||
<Card title="Requirements">
|
||||
- Go v1.23.1
|
||||
</Card>
|
||||
|
||||
```bash
|
||||
make local
|
||||
```
|
||||
176
development/feature-requests-and-bug-reports.mdx
Normal file
@@ -0,0 +1,176 @@
|
||||
---
|
||||
title: "Feature Requests & Bug Reports"
|
||||
description: "How to submit feature requests and report bugs for Pangolin"
|
||||
---
|
||||
|
||||
We welcome contributions from the community to help improve Pangolin. To ensure your feedback is properly tracked and prioritized, please use the appropriate GitHub sections for different types of submissions.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Feature Requests" icon="lightbulb">
|
||||
Submit in GitHub Discussions for community feedback and upvoting
|
||||
</Card>
|
||||
|
||||
<Card title="Bug Reports" icon="bug">
|
||||
Submit in GitHub Issues for tracking and resolution
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Feature Requests
|
||||
|
||||
<Info>
|
||||
**Use GitHub Discussions**: [Pangolin Discussions](https://github.com/fosrl/pangolin/discussions)
|
||||
</Info>
|
||||
|
||||
We encourage you to submit feature requests in the [GitHub Discussions section](https://github.com/fosrl/pangolin/discussions) of the Pangolin repository. This allows the community to:
|
||||
|
||||
- **Upvote features** they want to see implemented
|
||||
- **Provide feedback** and suggestions on proposed features
|
||||
- **Discuss implementation details** and alternatives
|
||||
- **Help prioritize** which features to work on next
|
||||
|
||||
### How to Submit a Feature Request
|
||||
|
||||
<Steps>
|
||||
<Step title="Check existing discussions">
|
||||
Search the [Discussions section](https://github.com/fosrl/pangolin/discussions) to see if your feature has already been requested.
|
||||
</Step>
|
||||
|
||||
<Step title="Create a new discussion">
|
||||
Click "New discussion" and select "Feature Requests" or "Ideas" as the category.
|
||||
</Step>
|
||||
|
||||
<Step title="Provide details">
|
||||
Include:
|
||||
- Clear description of the feature
|
||||
- Use case and benefits
|
||||
- Any implementation ideas
|
||||
- Screenshots or mockups if applicable
|
||||
</Step>
|
||||
|
||||
<Step title="Engage with community">
|
||||
Respond to comments and help refine the feature proposal.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Feature Request Template
|
||||
|
||||
```markdown
|
||||
## Feature Request: [Brief Description]
|
||||
|
||||
### What problem does this solve?
|
||||
[Describe the problem or limitation this feature would address]
|
||||
|
||||
### Proposed solution
|
||||
[Describe how this feature would work]
|
||||
|
||||
### Use cases
|
||||
[Describe specific scenarios where this would be useful]
|
||||
|
||||
### Additional context
|
||||
[Any other relevant information, screenshots, or examples]
|
||||
```
|
||||
|
||||
<Note>
|
||||
Feature requests with community support (upvotes and positive feedback) are more likely to be prioritized for development.
|
||||
</Note>
|
||||
|
||||
## Bug Reports
|
||||
|
||||
<Warning>
|
||||
**Use GitHub Issues**: [Pangolin Issues](https://github.com/fosrl/pangolin/issues)
|
||||
</Warning>
|
||||
|
||||
Bug reports should be submitted in the [GitHub Issues section](https://github.com/fosrl/pangolin/issues) for proper tracking and resolution. This ensures:
|
||||
|
||||
- **Proper tracking** of bugs through their lifecycle
|
||||
- **Developer visibility** for quick resolution
|
||||
- **Version tracking** and regression testing
|
||||
- **Duplicate detection** and consolidation
|
||||
|
||||
### How to Submit a Bug Report
|
||||
|
||||
<Steps>
|
||||
<Step title="Check existing issues">
|
||||
Search the [Issues section](https://github.com/fosrl/pangolin/issues) to see if the bug has already been reported.
|
||||
</Step>
|
||||
|
||||
<Step title="Create a new issue">
|
||||
Click "New issue" and select "Bug report" as the template.
|
||||
</Step>
|
||||
|
||||
<Step title="Fill out the template">
|
||||
Provide all requested information including:
|
||||
- Steps to reproduce
|
||||
- Expected vs actual behavior
|
||||
- Environment details
|
||||
- Error messages or logs
|
||||
</Step>
|
||||
|
||||
<Step title="Add labels">
|
||||
Use appropriate labels to help categorize the issue.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Bug Report Template
|
||||
|
||||
```markdown
|
||||
## Bug Description
|
||||
[Brief description of the bug]
|
||||
|
||||
## Steps to Reproduce
|
||||
1. [First step]
|
||||
2. [Second step]
|
||||
3. [Third step]
|
||||
|
||||
## Expected Behavior
|
||||
[What should happen]
|
||||
|
||||
## Actual Behavior
|
||||
[What actually happens]
|
||||
|
||||
## Environment
|
||||
- **Pangolin Version**: [version]
|
||||
- **OS**: [operating system]
|
||||
- **Browser**: [if applicable]
|
||||
- **Docker Version**: [if using Docker]
|
||||
|
||||
## Error Messages
|
||||
[Any error messages or logs]
|
||||
|
||||
## Additional Context
|
||||
[Screenshots, configuration files, or other relevant information]
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Please provide as much detail as possible to help developers reproduce and fix the issue quickly.
|
||||
</Warning>
|
||||
|
||||
## Before Submitting
|
||||
|
||||
<Check>
|
||||
- Search existing discussions and issues to avoid duplicates
|
||||
- Provide clear, detailed information
|
||||
- Include steps to reproduce (for bugs)
|
||||
- Test on the latest version of Pangolin
|
||||
- Check if the issue is environment-specific
|
||||
</Check>
|
||||
|
||||
## Alternative Channels
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Security Issues" icon="shield">
|
||||
For security vulnerabilities, please email security@fossorial.com instead of posting publicly.
|
||||
</Card>
|
||||
|
||||
<Card title="General Questions" icon="question">
|
||||
For general questions, use [GitHub Discussions](https://github.com/fosrl/pangolin/discussions) with the "Q&A" category, or come chat with us on [Discord](https://discord.gg/HCJR8Xhme4).
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Contributing Code
|
||||
|
||||
If you'd like to implement a feature or fix a bug yourself, please see our [Development Guide](/development/contributing) for setup instructions and coding guidelines.
|
||||
|
||||
<Note>
|
||||
We appreciate all contributions, whether they're feature requests, bug reports, or code contributions. Your feedback helps make Pangolin better for everyone.
|
||||
</Note>
|
||||
124
docs.json
@@ -1,68 +1,65 @@
|
||||
{
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"theme": "mint",
|
||||
"name": "Mint Starter Kit",
|
||||
"theme": "aspen",
|
||||
"name": "Pangolin Docs",
|
||||
"description": "Pangolin is a self-hosted alternative to Cloudflare Tunnels, designed to provide secure and highly-available ingress access to applications.",
|
||||
"colors": {
|
||||
"primary": "#16A34A",
|
||||
"light": "#07C983",
|
||||
"dark": "#15803D"
|
||||
"primary": "#F36117",
|
||||
"light": "#F36117",
|
||||
"dark": "#F36117"
|
||||
},
|
||||
"favicon": "/favicon.svg",
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Guides",
|
||||
"tab": "Docs",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting started",
|
||||
"group": "About",
|
||||
"pages": [
|
||||
"index",
|
||||
"quickstart",
|
||||
"development"
|
||||
"about/how-pangolin-works",
|
||||
"about/pangolin-vs-traditional-reverse-proxy",
|
||||
"about/pangolin-vs-vpn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Customization",
|
||||
"group": "Newt Sites",
|
||||
"pages": ["newt/installation"]
|
||||
},
|
||||
{
|
||||
"group": "Fully Self-host Pangolin",
|
||||
"pages": [
|
||||
"essentials/settings",
|
||||
"essentials/navigation"
|
||||
"self-host/quick-install",
|
||||
{
|
||||
"group": "Manual Installation",
|
||||
"pages": [
|
||||
"self-host/manual/docker-compose",
|
||||
"self-host/manual/unraid"
|
||||
]
|
||||
},
|
||||
"self-host/choosing-a-vps",
|
||||
"self-host/dns-and-networking",
|
||||
"self-host/how-to-update",
|
||||
"self-host/supporter-program",
|
||||
{
|
||||
"group": "Advanced Configuration",
|
||||
"pages": [
|
||||
"self-host/advanced/config-file",
|
||||
"self-host/advanced/wild-card-domains",
|
||||
"self-host/advanced/cloudflare-proxy",
|
||||
"self-host/advanced/without-tunneling",
|
||||
"self-host/advanced/container-cli-tool",
|
||||
"self-host/advanced/database-options",
|
||||
"self-host/advanced/integration-api"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Writing content",
|
||||
"group": "Development",
|
||||
"pages": [
|
||||
"essentials/markdown",
|
||||
"essentials/code",
|
||||
"essentials/images",
|
||||
"essentials/reusable-snippets"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "AI tools",
|
||||
"pages": [
|
||||
"ai-tools/cursor",
|
||||
"ai-tools/claude-code",
|
||||
"ai-tools/windsurf"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "API reference",
|
||||
"groups": [
|
||||
{
|
||||
"group": "API documentation",
|
||||
"pages": [
|
||||
"api-reference/introduction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Endpoint examples",
|
||||
"pages": [
|
||||
"api-reference/endpoint/get",
|
||||
"api-reference/endpoint/create",
|
||||
"api-reference/endpoint/delete",
|
||||
"api-reference/endpoint/webhook"
|
||||
"development/contributing",
|
||||
"development/feature-requests-and-bug-reports"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -71,45 +68,40 @@
|
||||
"global": {
|
||||
"anchors": [
|
||||
{
|
||||
"anchor": "Documentation",
|
||||
"href": "https://mintlify.com/docs",
|
||||
"icon": "book-open-cover"
|
||||
"anchor": "GitHub",
|
||||
"href": "https://github.com/fosrl/pangolin",
|
||||
"icon": "github"
|
||||
},
|
||||
{
|
||||
"anchor": "Community",
|
||||
"href": "https://mintlify.com/community",
|
||||
"icon": "slack"
|
||||
},
|
||||
{
|
||||
"anchor": "Blog",
|
||||
"href": "https://mintlify.com/blog",
|
||||
"icon": "newspaper"
|
||||
"anchor": "Discord",
|
||||
"href": "https://discord.gg/HCJR8Xhme4",
|
||||
"icon": "discord"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"logo": {
|
||||
"light": "/logo/light.svg",
|
||||
"dark": "/logo/dark.svg"
|
||||
"light": "/logo/light.png",
|
||||
"dark": "/logo/dark.png",
|
||||
"href": "https://docs.digpangolin.com"
|
||||
},
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"label": "Support",
|
||||
"href": "mailto:hi@mintlify.com"
|
||||
"label": "Contact Us",
|
||||
"href": "mailto:numbat@fossorial.io"
|
||||
}
|
||||
],
|
||||
"primary": {
|
||||
"type": "button",
|
||||
"label": "Dashboard",
|
||||
"href": "https://dashboard.mintlify.com"
|
||||
"label": "Pangolin Dashboard",
|
||||
"href": "https://pangolin.fossorial.io"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"x": "https://x.com/mintlify",
|
||||
"github": "https://github.com/mintlify",
|
||||
"linkedin": "https://linkedin.com/company/mintlify"
|
||||
"github": "https://github.com/fosrl/pangolin",
|
||||
"linkedin": "https://linkedin.com/company/digpangolin"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
title: 'Code blocks'
|
||||
description: 'Display inline code and code blocks'
|
||||
icon: 'code'
|
||||
---
|
||||
|
||||
## Inline code
|
||||
|
||||
To denote a `word` or `phrase` as code, enclose it in backticks (`).
|
||||
|
||||
```
|
||||
To denote a `word` or `phrase` as code, enclose it in backticks (`).
|
||||
```
|
||||
|
||||
## Code blocks
|
||||
|
||||
Use [fenced code blocks](https://www.markdownguide.org/extended-syntax/#fenced-code-blocks) by enclosing code in three backticks and follow the leading ticks with the programming language of your snippet to get syntax highlighting. Optionally, you can also write the name of your code after the programming language.
|
||||
|
||||
```java HelloWorld.java
|
||||
class HelloWorld {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello, World!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
````md
|
||||
```java HelloWorld.java
|
||||
class HelloWorld {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello, World!");
|
||||
}
|
||||
}
|
||||
```
|
||||
````
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
title: 'Images and embeds'
|
||||
description: 'Add image, video, and other HTML elements'
|
||||
icon: 'image'
|
||||
---
|
||||
|
||||
<img
|
||||
style={{ borderRadius: '0.5rem' }}
|
||||
src="https://mintlify-assets.b-cdn.net/bigbend.jpg"
|
||||
/>
|
||||
|
||||
## Image
|
||||
|
||||
### Using Markdown
|
||||
|
||||
The [markdown syntax](https://www.markdownguide.org/basic-syntax/#images) lets you add images using the following code
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
Note that the image file size must be less than 5MB. Otherwise, we recommend hosting on a service like [Cloudinary](https://cloudinary.com/) or [S3](https://aws.amazon.com/s3/). You can then use that URL and embed.
|
||||
|
||||
### Using embeds
|
||||
|
||||
To get more customizability with images, you can also use [embeds](/writing-content/embed) to add images
|
||||
|
||||
```html
|
||||
<img height="200" src="/path/image.jpg" />
|
||||
```
|
||||
|
||||
## Embeds and HTML elements
|
||||
|
||||
<iframe
|
||||
width="560"
|
||||
height="315"
|
||||
src="https://www.youtube.com/embed/4KzFe50RQkQ"
|
||||
title="YouTube video player"
|
||||
frameBorder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
style={{ width: '100%', borderRadius: '0.5rem' }}
|
||||
></iframe>
|
||||
|
||||
<br />
|
||||
|
||||
<Tip>
|
||||
|
||||
Mintlify supports [HTML tags in Markdown](https://www.markdownguide.org/basic-syntax/#html). This is helpful if you prefer HTML tags to Markdown syntax, and lets you create documentation with infinite flexibility.
|
||||
|
||||
</Tip>
|
||||
|
||||
### iFrames
|
||||
|
||||
Loads another HTML page within the document. Most commonly used for embedding videos.
|
||||
|
||||
```html
|
||||
<iframe src="https://www.youtube.com/embed/4KzFe50RQkQ"> </iframe>
|
||||
```
|
||||
@@ -1,88 +0,0 @@
|
||||
---
|
||||
title: 'Markdown syntax'
|
||||
description: 'Text, title, and styling in standard markdown'
|
||||
icon: 'text-size'
|
||||
---
|
||||
|
||||
## Titles
|
||||
|
||||
Best used for section headers.
|
||||
|
||||
```md
|
||||
## Titles
|
||||
```
|
||||
|
||||
### Subtitles
|
||||
|
||||
Best used for subsection headers.
|
||||
|
||||
```md
|
||||
### Subtitles
|
||||
```
|
||||
|
||||
<Tip>
|
||||
|
||||
Each **title** and **subtitle** creates an anchor and also shows up on the table of contents on the right.
|
||||
|
||||
</Tip>
|
||||
|
||||
## Text formatting
|
||||
|
||||
We support most markdown formatting. Simply add `**`, `_`, or `~` around text to format it.
|
||||
|
||||
| Style | How to write it | Result |
|
||||
| ------------- | ----------------- | --------------- |
|
||||
| Bold | `**bold**` | **bold** |
|
||||
| Italic | `_italic_` | _italic_ |
|
||||
| Strikethrough | `~strikethrough~` | ~strikethrough~ |
|
||||
|
||||
You can combine these. For example, write `**_bold and italic_**` to get **_bold and italic_** text.
|
||||
|
||||
You need to use HTML to write superscript and subscript text. That is, add `<sup>` or `<sub>` around your text.
|
||||
|
||||
| Text Size | How to write it | Result |
|
||||
| ----------- | ------------------------ | ---------------------- |
|
||||
| Superscript | `<sup>superscript</sup>` | <sup>superscript</sup> |
|
||||
| Subscript | `<sub>subscript</sub>` | <sub>subscript</sub> |
|
||||
|
||||
## Linking to pages
|
||||
|
||||
You can add a link by wrapping text in `[]()`. You would write `[link to google](https://google.com)` to [link to google](https://google.com).
|
||||
|
||||
Links to pages in your docs need to be root-relative. Basically, you should include the entire folder path. For example, `[link to text](/writing-content/text)` links to the page "Text" in our components section.
|
||||
|
||||
Relative links like `[link to text](../text)` will open slower because we cannot optimize them as easily.
|
||||
|
||||
## Blockquotes
|
||||
|
||||
### Singleline
|
||||
|
||||
To create a blockquote, add a `>` in front of a paragraph.
|
||||
|
||||
> Dorothy followed her through many of the beautiful rooms in her castle.
|
||||
|
||||
```md
|
||||
> Dorothy followed her through many of the beautiful rooms in her castle.
|
||||
```
|
||||
|
||||
### Multiline
|
||||
|
||||
> Dorothy followed her through many of the beautiful rooms in her castle.
|
||||
>
|
||||
> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood.
|
||||
|
||||
```md
|
||||
> Dorothy followed her through many of the beautiful rooms in her castle.
|
||||
>
|
||||
> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood.
|
||||
```
|
||||
|
||||
### LaTeX
|
||||
|
||||
Mintlify supports [LaTeX](https://www.latex-project.org) through the Latex component.
|
||||
|
||||
<Latex>8 x (vk x H1 - H2) = (0,1)</Latex>
|
||||
|
||||
```md
|
||||
<Latex>8 x (vk x H1 - H2) = (0,1)</Latex>
|
||||
```
|
||||
@@ -1,87 +0,0 @@
|
||||
---
|
||||
title: 'Navigation'
|
||||
description: 'The navigation field in docs.json defines the pages that go in the navigation menu'
|
||||
icon: 'map'
|
||||
---
|
||||
|
||||
The navigation menu is the list of links on every website.
|
||||
|
||||
You will likely update `docs.json` every time you add a new page. Pages do not show up automatically.
|
||||
|
||||
## Navigation syntax
|
||||
|
||||
Our navigation syntax is recursive which means you can make nested navigation groups. You don't need to include `.mdx` in page names.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```json Regular Navigation
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Docs",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": ["quickstart"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json Nested Navigation
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Docs",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": [
|
||||
"quickstart",
|
||||
{
|
||||
"group": "Nested Reference Pages",
|
||||
"pages": ["nested-reference-page"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Folders
|
||||
|
||||
Simply put your MDX files in folders and update the paths in `docs.json`.
|
||||
|
||||
For example, to have a page at `https://yoursite.com/your-folder/your-page` you would make a folder called `your-folder` containing an MDX file called `your-page.mdx`.
|
||||
|
||||
<Warning>
|
||||
|
||||
You cannot use `api` for the name of a folder unless you nest it inside another folder. Mintlify uses Next.js which reserves the top-level `api` folder for internal server calls. A folder name such as `api-reference` would be accepted.
|
||||
|
||||
</Warning>
|
||||
|
||||
```json Navigation With Folder
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Docs",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Group Name",
|
||||
"pages": ["your-folder/your-page"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Hidden pages
|
||||
|
||||
MDX files not included in `docs.json` will not show up in the sidebar but are accessible through the search bar and by linking directly to them.
|
||||
@@ -1,110 +0,0 @@
|
||||
---
|
||||
title: "Reusable snippets"
|
||||
description: "Reusable, custom snippets to keep content in sync"
|
||||
icon: "recycle"
|
||||
---
|
||||
|
||||
import SnippetIntro from '/snippets/snippet-intro.mdx';
|
||||
|
||||
<SnippetIntro />
|
||||
|
||||
## Creating a custom snippet
|
||||
|
||||
**Pre-condition**: You must create your snippet file in the `snippets` directory.
|
||||
|
||||
<Note>
|
||||
Any page in the `snippets` directory will be treated as a snippet and will not
|
||||
be rendered into a standalone page. If you want to create a standalone page
|
||||
from the snippet, import the snippet into another file and call it as a
|
||||
component.
|
||||
</Note>
|
||||
|
||||
### Default export
|
||||
|
||||
1. Add content to your snippet file that you want to re-use across multiple
|
||||
locations. Optionally, you can add variables that can be filled in via props
|
||||
when you import the snippet.
|
||||
|
||||
```mdx snippets/my-snippet.mdx
|
||||
Hello world! This is my content I want to reuse across pages. My keyword of the
|
||||
day is {word}.
|
||||
```
|
||||
|
||||
<Warning>
|
||||
The content that you want to reuse must be inside the `snippets` directory in
|
||||
order for the import to work.
|
||||
</Warning>
|
||||
|
||||
2. Import the snippet into your destination file.
|
||||
|
||||
```mdx destination-file.mdx
|
||||
---
|
||||
title: My title
|
||||
description: My Description
|
||||
---
|
||||
|
||||
import MySnippet from '/snippets/path/to/my-snippet.mdx';
|
||||
|
||||
## Header
|
||||
|
||||
Lorem impsum dolor sit amet.
|
||||
|
||||
<MySnippet word="bananas" />
|
||||
```
|
||||
|
||||
### Reusable variables
|
||||
|
||||
1. Export a variable from your snippet file:
|
||||
|
||||
```mdx snippets/path/to/custom-variables.mdx
|
||||
export const myName = 'my name';
|
||||
|
||||
export const myObject = { fruit: 'strawberries' };
|
||||
```
|
||||
|
||||
2. Import the snippet from your destination file and use the variable:
|
||||
|
||||
```mdx destination-file.mdx
|
||||
---
|
||||
title: My title
|
||||
description: My Description
|
||||
---
|
||||
|
||||
import { myName, myObject } from '/snippets/path/to/custom-variables.mdx';
|
||||
|
||||
Hello, my name is {myName} and I like {myObject.fruit}.
|
||||
```
|
||||
|
||||
### Reusable components
|
||||
|
||||
1. Inside your snippet file, create a component that takes in props by exporting
|
||||
your component in the form of an arrow function.
|
||||
|
||||
```mdx snippets/custom-component.mdx
|
||||
export const MyComponent = ({ title }) => (
|
||||
<div>
|
||||
<h1>{title}</h1>
|
||||
<p>... snippet content ...</p>
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
<Warning>
|
||||
MDX does not compile inside the body of an arrow function. Stick to HTML
|
||||
syntax when you can or use a default export if you need to use MDX.
|
||||
</Warning>
|
||||
|
||||
2. Import the snippet into your destination file and pass in the props
|
||||
|
||||
```mdx destination-file.mdx
|
||||
---
|
||||
title: My title
|
||||
description: My Description
|
||||
---
|
||||
|
||||
import { MyComponent } from '/snippets/custom-component.mdx';
|
||||
|
||||
Lorem ipsum dolor sit amet.
|
||||
|
||||
<MyComponent title={'Custom title'} />
|
||||
```
|
||||
@@ -1,318 +0,0 @@
|
||||
---
|
||||
title: 'Global Settings'
|
||||
description: 'Mintlify gives you complete control over the look and feel of your documentation using the docs.json file'
|
||||
icon: 'gear'
|
||||
---
|
||||
|
||||
Every Mintlify site needs a `docs.json` file with the core configuration settings. Learn more about the [properties](#properties) below.
|
||||
|
||||
## Properties
|
||||
|
||||
<ResponseField name="name" type="string" required>
|
||||
Name of your project. Used for the global title.
|
||||
|
||||
Example: `mintlify`
|
||||
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="navigation" type="Navigation[]" required>
|
||||
An array of groups with all the pages within that group
|
||||
<Expandable title="Navigation">
|
||||
<ResponseField name="group" type="string">
|
||||
The name of the group.
|
||||
|
||||
Example: `Settings`
|
||||
|
||||
</ResponseField>
|
||||
<ResponseField name="pages" type="string[]">
|
||||
The relative paths to the markdown files that will serve as pages.
|
||||
|
||||
Example: `["customization", "page"]`
|
||||
|
||||
</ResponseField>
|
||||
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="logo" type="string or object">
|
||||
Path to logo image or object with path to "light" and "dark" mode logo images
|
||||
<Expandable title="Logo">
|
||||
<ResponseField name="light" type="string">
|
||||
Path to the logo in light mode
|
||||
</ResponseField>
|
||||
<ResponseField name="dark" type="string">
|
||||
Path to the logo in dark mode
|
||||
</ResponseField>
|
||||
<ResponseField name="href" type="string" default="/">
|
||||
Where clicking on the logo links you to
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="favicon" type="string">
|
||||
Path to the favicon image
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="colors" type="Colors">
|
||||
Hex color codes for your global theme
|
||||
<Expandable title="Colors">
|
||||
<ResponseField name="primary" type="string" required>
|
||||
The primary color. Used for most often for highlighted content, section
|
||||
headers, accents, in light mode
|
||||
</ResponseField>
|
||||
<ResponseField name="light" type="string">
|
||||
The primary color for dark mode. Used for most often for highlighted
|
||||
content, section headers, accents, in dark mode
|
||||
</ResponseField>
|
||||
<ResponseField name="dark" type="string">
|
||||
The primary color for important buttons
|
||||
</ResponseField>
|
||||
<ResponseField name="background" type="object">
|
||||
The color of the background in both light and dark mode
|
||||
<Expandable title="Object">
|
||||
<ResponseField name="light" type="string" required>
|
||||
The hex color code of the background in light mode
|
||||
</ResponseField>
|
||||
<ResponseField name="dark" type="string" required>
|
||||
The hex color code of the background in dark mode
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="topbarLinks" type="TopbarLink[]">
|
||||
Array of `name`s and `url`s of links you want to include in the topbar
|
||||
<Expandable title="TopbarLink">
|
||||
<ResponseField name="name" type="string">
|
||||
The name of the button.
|
||||
|
||||
Example: `Contact us`
|
||||
</ResponseField>
|
||||
<ResponseField name="url" type="string">
|
||||
The url once you click on the button. Example: `https://mintlify.com/docs`
|
||||
</ResponseField>
|
||||
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="topbarCtaButton" type="Call to Action">
|
||||
<Expandable title="Topbar Call to Action">
|
||||
<ResponseField name="type" type={'"link" or "github"'} default="link">
|
||||
Link shows a button. GitHub shows the repo information at the url provided including the number of GitHub stars.
|
||||
</ResponseField>
|
||||
<ResponseField name="url" type="string">
|
||||
If `link`: What the button links to.
|
||||
|
||||
If `github`: Link to the repository to load GitHub information from.
|
||||
</ResponseField>
|
||||
<ResponseField name="name" type="string">
|
||||
Text inside the button. Only required if `type` is a `link`.
|
||||
</ResponseField>
|
||||
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="versions" type="string[]">
|
||||
Array of version names. Only use this if you want to show different versions
|
||||
of docs with a dropdown in the navigation bar.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="anchors" type="Anchor[]">
|
||||
An array of the anchors, includes the `icon`, `color`, and `url`.
|
||||
<Expandable title="Anchor">
|
||||
<ResponseField name="icon" type="string">
|
||||
The [Font Awesome](https://fontawesome.com/search?q=heart) icon used to feature the anchor.
|
||||
|
||||
Example: `comments`
|
||||
</ResponseField>
|
||||
<ResponseField name="name" type="string">
|
||||
The name of the anchor label.
|
||||
|
||||
Example: `Community`
|
||||
</ResponseField>
|
||||
<ResponseField name="url" type="string">
|
||||
The start of the URL that marks what pages go in the anchor. Generally, this is the name of the folder you put your pages in.
|
||||
</ResponseField>
|
||||
<ResponseField name="color" type="string">
|
||||
The hex color of the anchor icon background. Can also be a gradient if you pass an object with the properties `from` and `to` that are each a hex color.
|
||||
</ResponseField>
|
||||
<ResponseField name="version" type="string">
|
||||
Used if you want to hide an anchor until the correct docs version is selected.
|
||||
</ResponseField>
|
||||
<ResponseField name="isDefaultHidden" type="boolean" default="false">
|
||||
Pass `true` if you want to hide the anchor until you directly link someone to docs inside it.
|
||||
</ResponseField>
|
||||
<ResponseField name="iconType" default="duotone" type="string">
|
||||
One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin"
|
||||
</ResponseField>
|
||||
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="topAnchor" type="Object">
|
||||
Override the default configurations for the top-most anchor.
|
||||
<Expandable title="Object">
|
||||
<ResponseField name="name" default="Documentation" type="string">
|
||||
The name of the top-most anchor
|
||||
</ResponseField>
|
||||
<ResponseField name="icon" default="book-open" type="string">
|
||||
Font Awesome icon.
|
||||
</ResponseField>
|
||||
<ResponseField name="iconType" default="duotone" type="string">
|
||||
One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin"
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="tabs" type="Tabs[]">
|
||||
An array of navigational tabs.
|
||||
<Expandable title="Tabs">
|
||||
<ResponseField name="name" type="string">
|
||||
The name of the tab label.
|
||||
</ResponseField>
|
||||
<ResponseField name="url" type="string">
|
||||
The start of the URL that marks what pages go in the tab. Generally, this
|
||||
is the name of the folder you put your pages in.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="api" type="API">
|
||||
Configuration for API settings. Learn more about API pages at [API Components](/api-playground/demo).
|
||||
<Expandable title="API">
|
||||
<ResponseField name="baseUrl" type="string">
|
||||
The base url for all API endpoints. If `baseUrl` is an array, it will enable for multiple base url
|
||||
options that the user can toggle.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="auth" type="Auth">
|
||||
<Expandable title="Auth">
|
||||
<ResponseField name="method" type='"bearer" | "basic" | "key"'>
|
||||
The authentication strategy used for all API endpoints.
|
||||
</ResponseField>
|
||||
<ResponseField name="name" type="string">
|
||||
The name of the authentication parameter used in the API playground.
|
||||
|
||||
If method is `basic`, the format should be `[usernameName]:[passwordName]`
|
||||
</ResponseField>
|
||||
<ResponseField name="inputPrefix" type="string">
|
||||
The default value that's designed to be a prefix for the authentication input field.
|
||||
|
||||
E.g. If an `inputPrefix` of `AuthKey` would inherit the default input result of the authentication field as `AuthKey`.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="playground" type="Playground">
|
||||
Configurations for the API playground
|
||||
|
||||
<Expandable title="Playground">
|
||||
<ResponseField name="mode" default="show" type='"show" | "simple" | "hide"'>
|
||||
Whether the playground is showing, hidden, or only displaying the endpoint with no added user interactivity `simple`
|
||||
|
||||
Learn more at the [playground guides](/api-playground/demo)
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="maintainOrder" type="boolean">
|
||||
Enabling this flag ensures that key ordering in OpenAPI pages matches the key ordering defined in the OpenAPI file.
|
||||
|
||||
<Warning>This behavior will soon be enabled by default, at which point this field will be deprecated.</Warning>
|
||||
</ResponseField>
|
||||
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="openapi" type="string | string[]">
|
||||
A string or an array of strings of URL(s) or relative path(s) pointing to your
|
||||
OpenAPI file.
|
||||
|
||||
Examples:
|
||||
<CodeGroup>
|
||||
```json Absolute
|
||||
"openapi": "https://example.com/openapi.json"
|
||||
```
|
||||
```json Relative
|
||||
"openapi": "/openapi.json"
|
||||
```
|
||||
```json Multiple
|
||||
"openapi": ["https://example.com/openapi1.json", "/openapi2.json", "/openapi3.json"]
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="footerSocials" type="FooterSocials">
|
||||
An object of social media accounts where the key:property pair represents the social media platform and the account url.
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"x": "https://x.com/mintlify",
|
||||
"website": "https://mintlify.com"
|
||||
}
|
||||
```
|
||||
<Expandable title="FooterSocials">
|
||||
<ResponseField name="[key]" type="string">
|
||||
One of the following values `website`, `facebook`, `x`, `discord`, `slack`, `github`, `linkedin`, `instagram`, `hacker-news`
|
||||
|
||||
Example: `x`
|
||||
</ResponseField>
|
||||
<ResponseField name="property" type="string">
|
||||
The URL to the social platform.
|
||||
|
||||
Example: `https://x.com/mintlify`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="feedback" type="Feedback">
|
||||
Configurations to enable feedback buttons
|
||||
|
||||
<Expandable title="Feedback">
|
||||
<ResponseField name="suggestEdit" type="boolean" default="false">
|
||||
Enables a button to allow users to suggest edits via pull requests
|
||||
</ResponseField>
|
||||
<ResponseField name="raiseIssue" type="boolean" default="false">
|
||||
Enables a button to allow users to raise an issue about the documentation
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="modeToggle" type="ModeToggle">
|
||||
Customize the dark mode toggle.
|
||||
<Expandable title="ModeToggle">
|
||||
<ResponseField name="default" type={'"light" or "dark"'}>
|
||||
Set if you always want to show light or dark mode for new users. When not
|
||||
set, we default to the same mode as the user's operating system.
|
||||
</ResponseField>
|
||||
<ResponseField name="isHidden" type="boolean" default="false">
|
||||
Set to true to hide the dark/light mode toggle. You can combine `isHidden` with `default` to force your docs to only use light or dark mode. For example:
|
||||
|
||||
<CodeGroup>
|
||||
```json Only Dark Mode
|
||||
"modeToggle": {
|
||||
"default": "dark",
|
||||
"isHidden": true
|
||||
}
|
||||
```
|
||||
|
||||
```json Only Light Mode
|
||||
"modeToggle": {
|
||||
"default": "light",
|
||||
"isHidden": true
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</ResponseField>
|
||||
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="backgroundImage" type="string">
|
||||
A background image to be displayed behind every page. See example with
|
||||
[Infisical](https://infisical.com/docs) and [FRPC](https://frpc.io).
|
||||
</ResponseField>
|
||||
39
favicon.svg
@@ -1,19 +1,22 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.06145 23.1079C5.26816 22.3769 -3.39077 20.6274 1.4173 5.06384C9.6344 6.09939 16.9728 14.0644 9.06145 23.1079Z" fill="url(#paint0_linear_17557_2021)"/>
|
||||
<path d="M8.91928 23.0939C5.27642 21.2223 0.78371 4.20891 17.0071 0C20.7569 7.19341 19.6212 16.5452 8.91928 23.0939Z" fill="url(#paint1_linear_17557_2021)"/>
|
||||
<path d="M8.91388 23.0788C8.73534 19.8817 10.1585 9.08525 23.5699 13.1107C23.1812 20.1229 18.984 26.4182 8.91388 23.0788Z" fill="url(#paint2_linear_17557_2021)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_17557_2021" x1="3.77557" y1="5.91571" x2="5.23185" y2="21.5589" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#18E299"/>
|
||||
<stop offset="1" stop-color="#15803D"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_17557_2021" x1="12.1711" y1="-0.718425" x2="10.1897" y2="22.9832" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#16A34A"/>
|
||||
<stop offset="1" stop-color="#4ADE80"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_17557_2021" x1="23.1327" y1="15.353" x2="9.33841" y2="18.5196" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4ADE80"/>
|
||||
<stop offset="1" stop-color="#0D9373"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="900.82861"
|
||||
height="955.20648"
|
||||
viewBox="0 0 238.34422 252.7317"
|
||||
version="1.1"
|
||||
id="svg420"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs417" />
|
||||
<g
|
||||
id="layer1"
|
||||
transform="translate(-13.119542,-5.9258171)">
|
||||
<path
|
||||
d="m 213.66176,90.072122 c 4.95655,0 8.97383,4.018046 8.97383,8.973827 0,4.956581 -4.01728,8.974621 -8.97383,8.974621 -4.95657,0 -8.97462,-4.01804 -8.97462,-8.974621 0,-4.955781 4.01805,-8.973827 8.97462,-8.973827 z m 35.2316,37.450998 c -0.90048,29.80928 -23.66033,69.21262 -54.51292,79.34466 -36.04206,11.836 -63.40991,-5.92226 -72.08409,-26.74061 -6.75754,-16.21966 -1.65117,-35.62363 10.96266,-43.83669 10.6506,-6.93533 30.48543,-8.76736 47.15454,2.19144 -5.85627,-15.34246 -21.62491,-25.4256 -35.59101,-28.49424 -13.96613,-3.06867 -28.38324,0.43858 -38.74504,5.69946 13.29071,-14.68572 44.40801,-28.946049 78.24077,-10.95958 22.67676,12.05491 32.43775,28.93208 42.0489,51.72763 C 251.59637,117.87858 234.026,71.411066 203.39074,43.794029 172.15544,15.636686 129.95516,4.340214 97.668803,6.103155 108.32483,12.678273 120.84625,22.06586 132.41209,33.053363 81.298533,26.697169 39.174705,38.314245 13.119542,73.749217 27.67508,70.878527 46.868833,69.073666 65.974711,70.016861 28.737658,96.252107 7.1124298,140.38147 18.105298,186.43137 c 6.718497,-11.74129 16.767711,-25.84558 28.726275,-38.62863 -3.677175,34.36994 1.42836,80.83745 45.62293,110.85478 -2.25587,-9.42394 -4.08014,-20.88443 -4.91466,-33.0154 20.673197,16.1282 50.685067,29.42205 87.917917,20.24096 65.77679,-16.21975 83.34719,-79.78335 73.4356,-118.35996"
|
||||
style="fill:#f36118;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0776283"
|
||||
id="path32" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
images/fossorial-dashboard.png
Normal file
|
After Width: | Height: | Size: 491 KiB |
BIN
images/gerbil_config.png
Normal file
|
After Width: | Height: | Size: 377 KiB |
BIN
images/gerbil_logs.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
images/pangolin_config.png
Normal file
|
After Width: | Height: | Size: 269 KiB |
BIN
images/redeem-key.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
images/supporter-tiers.png
Normal file
|
After Width: | Height: | Size: 751 KiB |
BIN
images/swagger.png
Normal file
|
After Width: | Height: | Size: 442 KiB |
4
images/system-diagram.svg
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
images/traefik_config.png
Normal file
|
After Width: | Height: | Size: 452 KiB |
BIN
images/traefik_networking.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
images/traefik_repo.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
images/unraid_store.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
100
index.mdx
@@ -1,97 +1,43 @@
|
||||
---
|
||||
title: "Introduction"
|
||||
description: "Welcome to the new home for your documentation"
|
||||
title: "Introduction to Pangolin"
|
||||
---
|
||||
|
||||
## Setting up
|
||||
Pangolin is an open source and self-hostable tunneled reverse proxy server with identity-aware access control. Think self-hosted Cloudflare Tunnels.
|
||||
|
||||
Get your documentation site up and running in minutes.
|
||||
|
||||
<Card
|
||||
title="Start here"
|
||||
icon="rocket"
|
||||
href="/quickstart"
|
||||
horizontal
|
||||
>
|
||||
Follow our three step quickstart guide.
|
||||
<Card img="/images/fossorial-dashboard.png">
|
||||
Screenshot of resources page from the Pangolin Dashboard.
|
||||
</Card>
|
||||
|
||||
## Make it yours
|
||||
Pangolin establishes secure connections from edge networks to cloud exit nodes, bypassing the need for public inbound ports and complex firewall configurations. Pangolin is incredibly useful for exposing local services, IoT devices, or internal applications to the internet without direct exposure, enhancing security by reducing attack surface and simplifying network management. Additionally, Pangolin acts as an identity-aware proxy by authenticating every request against admin-defined access controls and rules.
|
||||
|
||||
Design a docs site that looks great and empowers your users.
|
||||
<Columns cols={2}>
|
||||
<Card title="How it works" icon="map" href="/about/how-pangolin-works">
|
||||
Learn how the Pangolin system works from the server to the edge network.
|
||||
</Card>
|
||||
<Card title="Use cases" icon="brain" href="/about/how-pangolin-works">
|
||||
Learn about the many different ways you can use Pangolin's capabilities.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card
|
||||
title="Edit locally"
|
||||
icon="pen-to-square"
|
||||
href="/development"
|
||||
title="Self-host Pangolin"
|
||||
icon="server"
|
||||
href="/self-host/quick-install"
|
||||
>
|
||||
Edit your docs locally and preview them in real time.
|
||||
Install and manage the Pangolin server on your own infrastructure.
|
||||
</Card>
|
||||
<Card
|
||||
title="Customize your site"
|
||||
icon="palette"
|
||||
title="Install Newt"
|
||||
icon="sitemap"
|
||||
href="/essentials/settings"
|
||||
>
|
||||
Customize the design and colors of your site to match your brand.
|
||||
</Card>
|
||||
<Card
|
||||
title="Set up navigation"
|
||||
icon="map"
|
||||
href="/essentials/navigation"
|
||||
>
|
||||
Organize your docs to help users find what they need and succeed with your product.
|
||||
</Card>
|
||||
<Card
|
||||
title="API documentation"
|
||||
icon="terminal"
|
||||
href="/api-reference/introduction"
|
||||
>
|
||||
Auto-generate API documentation from OpenAPI specifications.
|
||||
Install Newt, the edge client, almost anywhere.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
## Create beautiful pages
|
||||
## What is a fossorial animal?
|
||||
|
||||
Everything you need to create world-class documentation.
|
||||
The Pangolin system is made up of many components, all with unique animal names. These animals are called fossorial animals.
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card
|
||||
title="Write with MDX"
|
||||
icon="pen-fancy"
|
||||
href="/essentials/markdown"
|
||||
>
|
||||
Use MDX to style your docs pages.
|
||||
</Card>
|
||||
<Card
|
||||
title="Code samples"
|
||||
icon="code"
|
||||
href="/essentials/code"
|
||||
>
|
||||
Add sample code to demonstrate how to use your product.
|
||||
</Card>
|
||||
<Card
|
||||
title="Images"
|
||||
icon="image"
|
||||
href="/essentials/images"
|
||||
>
|
||||
Display images and other media.
|
||||
</Card>
|
||||
<Card
|
||||
title="Reusable snippets"
|
||||
icon="recycle"
|
||||
href="/essentials/reusable-snippets"
|
||||
>
|
||||
Write once and reuse across your docs.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
## Need inspiration?
|
||||
|
||||
<Card
|
||||
title="See complete examples"
|
||||
icon="stars"
|
||||
href="https://mintlify.com/customers"
|
||||
>
|
||||
Browse our showcase of exceptional documentation sites.
|
||||
</Card>
|
||||
A fossorial animal is one adapted to digging which lives primarily but not solely, underground. Some examples are badgers, naked mole-rats, clams, meerkats, and mole salamanders, as well as many beetles, wasps, and bees.
|
||||
|
||||
15
lib/fetchLatestRelease.js
Normal file
@@ -0,0 +1,15 @@
|
||||
export async function fetchLatestRelease(repo) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://api.github.com/repos/${repo}/releases/latest`,
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch release info: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
const latestVersion = data.tag_name;
|
||||
return latestVersion;
|
||||
} catch (error) {
|
||||
console.error("Error fetching latest release:", error);
|
||||
}
|
||||
}
|
||||
BIN
logo/.DS_Store
vendored
Normal file
BIN
logo/dark.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 12 KiB |
BIN
logo/light.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 12 KiB |
163
newt/installation.mdx
Normal file
@@ -0,0 +1,163 @@
|
||||
---
|
||||
title: "Installation"
|
||||
description: "Install Newt VPN client as a binary or Docker container"
|
||||
---
|
||||
|
||||
Newt can be installed as either a static binary executable or a Docker container. Configuration is passed via CLI arguments in both cases.
|
||||
|
||||
<Warning>
|
||||
You **must first create a site and copy the Newt config** in Pangolin before running Newt.
|
||||
</Warning>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Binary Installation" icon="download">
|
||||
- Static executable
|
||||
- Cross-platform support
|
||||
- Easy to install and run
|
||||
- Systemd service support
|
||||
</Card>
|
||||
|
||||
<Card title="Docker Installation" icon="docker">
|
||||
- Containerized deployment
|
||||
- Environment variables
|
||||
- Docker Compose support
|
||||
- Easy management
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Binary Installation
|
||||
|
||||
### Quick Install (Recommended)
|
||||
|
||||
Use this command to automatically install Newt. It detects your system architecture automatically and always pulls the latest version, adding Newt to your PATH:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://docs.fossorial.io/get-newt.sh | bash
|
||||
```
|
||||
|
||||
### Manual Download
|
||||
|
||||
Binaries for Linux, macOS, and Windows are available in the [GitHub releases](https://github.com/fosrl/newt/releases) for ARM and AMD64 (x86_64) architectures.
|
||||
|
||||
Download and install manually:
|
||||
|
||||
```bash
|
||||
wget -O newt "https://github.com/fosrl/newt/releases/download/{version}/newt_{architecture}" && chmod +x ./newt
|
||||
```
|
||||
|
||||
<Note>
|
||||
Replace `{version}` with the desired version and `{architecture}` with your architecture. Check the [release notes](https://github.com/fosrl/newt/releases) for the latest information.
|
||||
</Note>
|
||||
|
||||
### Running Newt
|
||||
|
||||
Run Newt with the configuration from Pangolin:
|
||||
|
||||
```bash
|
||||
newt \
|
||||
--id 31frd0uzbjvp721 \
|
||||
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
|
||||
--endpoint https://example.com
|
||||
```
|
||||
|
||||
### Permanent Installation
|
||||
|
||||
Install to your PATH (may need to run as root):
|
||||
|
||||
```bash
|
||||
mv ./newt /usr/local/bin
|
||||
```
|
||||
|
||||
<Note>
|
||||
The quick installer will do this step for you.
|
||||
</Note>
|
||||
|
||||
### Systemd Service
|
||||
|
||||
Create a basic systemd service:
|
||||
|
||||
```ini title="/etc/systemd/system/newt.service"
|
||||
[Unit]
|
||||
Description=Newt
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/newt --id 31frd0uzbjvp721 --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 --endpoint https://example.com
|
||||
Restart=always
|
||||
User=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Make sure to move the binary to `/usr/local/bin/newt` before creating the service!
|
||||
</Warning>
|
||||
|
||||
## Docker Installation
|
||||
|
||||
### Pull the Image
|
||||
|
||||
Pull the latest Newt image from Docker Hub:
|
||||
|
||||
```bash
|
||||
docker pull fosrl/newt:latest
|
||||
```
|
||||
|
||||
### Run with Docker
|
||||
|
||||
Run Newt with CLI arguments from Pangolin:
|
||||
|
||||
```bash
|
||||
docker run -it fosrl/newt --id 31frd0uzbjvp721 \
|
||||
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
|
||||
--endpoint https://example.com
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
#### Environment Variables (Recommended)
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
newt:
|
||||
image: fosrl/newt
|
||||
container_name: newt
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PANGOLIN_ENDPOINT=https://example.com
|
||||
- NEWT_ID=2ix2t8xk22ubpfy
|
||||
- NEWT_SECRET=nnisrfsdfc7prqsp9ewo1dvtvci50j5uiqotez00dgap0ii2
|
||||
```
|
||||
|
||||
#### CLI Arguments
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
newt:
|
||||
image: fosrl/newt
|
||||
container_name: newt
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --id 31frd0uzbjvp721
|
||||
- --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6
|
||||
- --endpoint https://example.com
|
||||
```
|
||||
|
||||
Start the service:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Platform-Specific Installation
|
||||
|
||||
### Unraid
|
||||
|
||||
Newt is available in the Unraid Community Applications store. Search for "Newt" and follow the installation prompts. Enter the ID, secret, and endpoint from Pangolin in the template fields.
|
||||
|
||||
<img src="/images/unraid_store.png" alt="Newt on CA" />
|
||||
|
||||
### Portainer and Other UIs
|
||||
|
||||
Container management UIs like Portainer typically allow passing commands and environment variables to containers similar to Docker Compose. Look for a commands or arguments configuration section and follow the relevant guides.
|
||||
6
package-lock.json
generated
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "docs-v2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
title: "Quickstart"
|
||||
description: "Start building awesome documentation in minutes"
|
||||
---
|
||||
|
||||
## Get started in three steps
|
||||
|
||||
Get your documentation site running locally and make your first customization.
|
||||
|
||||
### Step 1: Set up your local environment
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion icon="copy" title="Clone your docs locally">
|
||||
During the onboarding process, you created a GitHub repository with your docs content if you didn't already have one. You can find a link to this repository in your [dashboard](https://dashboard.mintlify.com).
|
||||
|
||||
To clone the repository locally so that you can make and preview changes to your docs, follow the [Cloning a repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) guide in the GitHub docs.
|
||||
</Accordion>
|
||||
<Accordion icon="rectangle-terminal" title="Start the preview server">
|
||||
1. Install the Mintlify CLI: `npm i -g mint`
|
||||
2. Navigate to your docs directory and run: `mint dev`
|
||||
3. Open `http://localhost:3000` to see your docs live!
|
||||
|
||||
<Tip>Your preview updates automatically as you edit files.</Tip>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Step 2: Deploy your changes
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion icon="github" title="Install our GitHub app">
|
||||
Install the Mintlify GitHub app from your [dashboard](https://dashboard.mintlify.com/settings/organization/github-app).
|
||||
|
||||
Our GitHub app automatically deploys your changes to your docs site, so you don't need to manage deployments yourself.
|
||||
</Accordion>
|
||||
<Accordion icon="palette" title="Update your site name and colors">
|
||||
For a first change, let's update the name and colors of your docs site.
|
||||
|
||||
1. Open `docs.json` in your editor.
|
||||
2. Change the `"name"` field to your project name.
|
||||
3. Update the `"colors"` to match your brand.
|
||||
4. Save and see your changes instantly at `http://localhost:3000`.
|
||||
|
||||
<Tip>Try changing the primary color to see an immediate difference!</Tip>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Step 3: Go live
|
||||
|
||||
<Accordion icon="rocket" title="Publish your docs">
|
||||
1. Commit and push your changes.
|
||||
2. Your docs will update and be live in moments!
|
||||
</Accordion>
|
||||
|
||||
## Next steps
|
||||
|
||||
Now that you have your docs running, explore these key features:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
|
||||
<Card title="Write Content" icon="pen-to-square" href="/essentials/markdown">
|
||||
Learn MDX syntax and start writing your documentation.
|
||||
</Card>
|
||||
|
||||
<Card title="Customize style" icon="palette" href="/essentials/settings">
|
||||
Make your docs match your brand perfectly.
|
||||
</Card>
|
||||
|
||||
<Card title="Add code examples" icon="square-code" href="/essentials/code">
|
||||
Include syntax-highlighted code blocks.
|
||||
</Card>
|
||||
|
||||
<Card title="API documentation" icon="code" href="/api-reference/introduction">
|
||||
Auto-generate API docs from OpenAPI specs.
|
||||
</Card>
|
||||
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
**Need help?** See our [full documentation](https://mintlify.com/docs) or join our [community](https://mintlify.com/community).
|
||||
</Note>
|
||||
BIN
self-host/.DS_Store
vendored
Normal file
56
self-host/advanced/cloudflare-proxy.mdx
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: "Cloudflare Proxy"
|
||||
---
|
||||
|
||||
Pangolin works with Cloudflare proxy (orange cloud) enabled, but requires specific configuration:
|
||||
|
||||
<Warning>
|
||||
**Terms of Service**: Enabling Cloudflare proxy binds you to Cloudflare's terms of service as traffic routes through their network.
|
||||
</Warning>
|
||||
|
||||
### SSL Configuration
|
||||
|
||||
**Recommended setup:**
|
||||
1. **Use wildcard certificates** with DNS-01 challenge
|
||||
2. **Set SSL/TLS mode to Full (Strict)**
|
||||
3. **Disable port 80** (not needed with wildcard certs)
|
||||
|
||||
<Info>
|
||||
Pangolin will **not work** with Cloudflare's Full or Automatic SSL/TLS modes. Only Full (Strict) mode is supported.
|
||||
</Info>
|
||||
|
||||
### WireGuard Configuration
|
||||
|
||||
Since Cloudflare proxy obscures the destination IP, you must explicitly set your VPS IP in the [config file](/self-host/advanced/config-file):
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
base_endpoint: "YOUR_VPS_IP_ADDRESS" # Required with Cloudflare proxy
|
||||
```
|
||||
|
||||
<Steps>
|
||||
<Step title="Get your VPS IP">
|
||||
Find your VPS public IP address:
|
||||
|
||||
```bash
|
||||
curl ifconfig.io
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Update configuration">
|
||||
Add the IP to your `config.yml`:
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
base_endpoint: "104.21.16.1" # Replace with your actual IP
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Restart services">
|
||||
Restart Pangolin to apply the changes:
|
||||
|
||||
```bash
|
||||
docker-compose restart
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
566
self-host/advanced/config-file.mdx
Normal file
@@ -0,0 +1,566 @@
|
||||
---
|
||||
title: "Configuration File"
|
||||
description: "Configure Pangolin using the config.yml file with detailed settings for all components"
|
||||
---
|
||||
|
||||
The `config.yml` file controls all aspects of your Pangolin deployment, including server settings, domain configuration, email setup, and security options. This file is mounted at `config/config.yml` in your Docker container.
|
||||
|
||||
## Setting up your `config.yml`
|
||||
|
||||
To get started, create a basic configuration file with the essential settings:
|
||||
|
||||
Minimal Pangolin configuration:
|
||||
|
||||
```yaml title="config.yml"
|
||||
app:
|
||||
dashboard_url: "http://pangolin.example.com"
|
||||
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: "pangolin.example.com"
|
||||
cert_resolver: "letsencrypt"
|
||||
|
||||
server:
|
||||
secret: "your-strong-secret"
|
||||
|
||||
gerbil:
|
||||
base_endpoint: "pangolin.example.com"
|
||||
|
||||
flags:
|
||||
require_email_verification: false
|
||||
disable_signup_without_invite: true
|
||||
disable_user_create_org: true
|
||||
```
|
||||
|
||||
|
||||
<Warning>
|
||||
Generate a strong secret for `server.secret`. Use at least 32 characters with a mix of letters, numbers, and special characters.
|
||||
</Warning>
|
||||
|
||||
## Reference
|
||||
|
||||
This section contains the complete reference for all configuration options in `config.yml`.
|
||||
|
||||
### Application Settings
|
||||
|
||||
<ResponseField name="app" type="object" required>
|
||||
Core application configuration including dashboard URL, logging, and general settings.
|
||||
|
||||
<Expandable title="App">
|
||||
<ResponseField name="dashboard_url" type="string" required>
|
||||
The URL where your Pangolin dashboard is hosted.
|
||||
|
||||
**Examples**: `https://example.com`, `https://pangolin.example.com`
|
||||
|
||||
This URL is used for generating links, redirects, and authentication flows. You can run Pangolin on a subdomain or root domain.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="log_level" type="string">
|
||||
The logging level for the application.
|
||||
|
||||
**Options**: `debug`, `info`, `warn`, `error`
|
||||
|
||||
**Default**: `info`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="save_logs" type="boolean">
|
||||
Whether to save logs to files in the `config/logs/` directory.
|
||||
|
||||
**Default**: `false`
|
||||
|
||||
<Note>
|
||||
When enabled, logs rotate automatically:
|
||||
- Max file size: 20MB
|
||||
- Max files: 7 days
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="log_failed_attempts" type="boolean">
|
||||
Whether to log failed authentication attempts for security monitoring.
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Server Configuration
|
||||
|
||||
<ResponseField name="server" type="object" required>
|
||||
Server ports, networking, and authentication settings.
|
||||
|
||||
<Expandable title="Server">
|
||||
<ResponseField name="external_port" type="integer">
|
||||
The port for the front-end API that handles external requests.
|
||||
|
||||
**Example**: `3000`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="internal_port" type="integer">
|
||||
The port for the internal private-facing API.
|
||||
|
||||
**Example**: `3001`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="next_port" type="integer">
|
||||
The port for the frontend server (Next.js).
|
||||
|
||||
**Example**: `3002`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="integration_port" type="integer">
|
||||
The port for the integration API (optional).
|
||||
|
||||
**Example**: `3003`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="internal_hostname" type="string">
|
||||
The hostname of the Pangolin container for internal communication.
|
||||
|
||||
**Example**: `pangolin`
|
||||
|
||||
<Tip>
|
||||
If using Docker Compose, this should match your container name.
|
||||
</Tip>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="session_cookie_name" type="string">
|
||||
The name of the session cookie for storing authentication tokens.
|
||||
|
||||
**Example**: `p_session_token`
|
||||
|
||||
**Default**: `p_session_token`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="resource_access_token_param" type="string">
|
||||
Query parameter name for passing access tokens in requests.
|
||||
|
||||
**Example**: `p_token`
|
||||
|
||||
**Default**: `p_token`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="resource_access_token_headers" type="object">
|
||||
HTTP headers for passing access tokens in requests.
|
||||
|
||||
<Expandable title="Headers">
|
||||
<ResponseField name="id" type="string">
|
||||
Header name for access token ID.
|
||||
|
||||
**Example**: `P-Access-Token-Id`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="token" type="string">
|
||||
Header name for access token.
|
||||
|
||||
**Example**: `P-Access-Token`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="resource_session_request_param" type="string">
|
||||
Query parameter for session request tokens.
|
||||
|
||||
**Example**: `p_session_request`
|
||||
|
||||
**Default**: `p_session_request`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="cors" type="object">
|
||||
Cross-Origin Resource Sharing (CORS) configuration.
|
||||
|
||||
<Expandable title="CORS">
|
||||
<ResponseField name="origins" type="array of strings">
|
||||
Allowed origins for cross-origin requests.
|
||||
|
||||
**Example**: `["https://pangolin.example.com"]`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="methods" type="array of strings">
|
||||
Allowed HTTP methods for CORS requests.
|
||||
|
||||
**Example**: `["GET", "POST", "PUT", "DELETE", "PATCH"]`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="allowed_headers" type="array of strings">
|
||||
Allowed HTTP headers in CORS requests.
|
||||
|
||||
**Example**: `["X-CSRF-Token", "Content-Type"]`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="credentials" type="boolean">
|
||||
Whether to allow credentials in CORS requests.
|
||||
|
||||
**Default**: `true`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="trust_proxy" type="integer">
|
||||
Number of proxy headers to trust for client IP detection.
|
||||
|
||||
**Example**: `1`
|
||||
|
||||
**Default**: `1`
|
||||
|
||||
<Tip>
|
||||
Use `1` if running behind a single reverse proxy like Traefik.
|
||||
</Tip>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="dashboard_session_length_hours" type="integer">
|
||||
Dashboard session duration in hours.
|
||||
|
||||
**Example**: `720` (30 days)
|
||||
|
||||
**Default**: `720`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="resource_session_length_hours" type="integer">
|
||||
Resource session duration in hours.
|
||||
|
||||
**Example**: `720` (30 days)
|
||||
|
||||
**Default**: `720`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="secret" type="string" required>
|
||||
Secret key for encrypting sensitive data.
|
||||
|
||||
**Environment Variable**: `SERVER_SECRET`
|
||||
|
||||
**Minimum Length**: 8 characters
|
||||
|
||||
**Example**: `"d28@a2b.2HFTe2bMtZHGneNYgQFKT2X4vm4HuXUXBcq6aVyNZjdGt6Dx-_A@9b3y"`
|
||||
|
||||
<Warning>
|
||||
Generate a strong, random secret. This is used for encrypting sensitive data and should be kept secure.
|
||||
</Warning>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Domain Configuration
|
||||
|
||||
<ResponseField name="domains" type="object" required>
|
||||
Domain settings for SSL certificates and routing.
|
||||
|
||||
At least one domain must be configured.
|
||||
|
||||
<Expandable title="Domains">
|
||||
<ResponseField name="<domain_key>" type="object">
|
||||
Domain configuration with a unique key of your choice.
|
||||
|
||||
<Expandable title="Domain Settings">
|
||||
<ResponseField name="base_domain" type="string" required>
|
||||
The base domain for this configuration.
|
||||
|
||||
**Example**: `example.com`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="cert_resolver" type="string" required>
|
||||
The Traefik certificate resolver name.
|
||||
|
||||
**Example**: `letsencrypt`
|
||||
|
||||
<Note>
|
||||
This must match the certificate resolver name in your Traefik configuration.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="prefer_wildcard_cert" type="boolean">
|
||||
Whether to prefer wildcard certificates for this domain.
|
||||
|
||||
**Example**: `true`
|
||||
|
||||
<Tip>
|
||||
Useful for domains with many subdomains to reduce certificate management overhead.
|
||||
</Tip>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Traefik Integration
|
||||
|
||||
<ResponseField name="traefik" type="object">
|
||||
Traefik reverse proxy configuration settings.
|
||||
|
||||
<Expandable title="Traefik">
|
||||
<ResponseField name="http_entrypoint" type="string">
|
||||
The Traefik entrypoint name for HTTP traffic.
|
||||
|
||||
**Example**: `web`
|
||||
|
||||
<Note>
|
||||
Must match the entrypoint name in your Traefik configuration.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="https_entrypoint" type="string">
|
||||
The Traefik entrypoint name for HTTPS traffic.
|
||||
|
||||
**Example**: `websecure`
|
||||
|
||||
<Note>
|
||||
Must match the entrypoint name in your Traefik configuration.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="cert_resolver" type="string">
|
||||
The default certificate resolver for domains created through the UI.
|
||||
|
||||
**Example**: `letsencrypt`
|
||||
|
||||
<Note>
|
||||
This only applies to domains created through the Pangolin dashboard.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="prefer_wildcard_cert" type="boolean">
|
||||
Whether to prefer wildcard certificates for UI-created domains.
|
||||
|
||||
**Example**: `true`
|
||||
|
||||
<Note>
|
||||
This only applies to domains created through the Pangolin dashboard.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="additional_middlewares" type="array of strings">
|
||||
Additional Traefik middlewares to apply to resource routers.
|
||||
|
||||
**Example**: `["middleware1", "middleware2"]`
|
||||
|
||||
<Note>
|
||||
These middlewares must be defined in your Traefik dynamic configuration.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Gerbil Tunnel Controller
|
||||
|
||||
<ResponseField name="gerbil" type="object" required>
|
||||
Gerbil tunnel controller settings for WireGuard tunneling.
|
||||
|
||||
<Expandable title="Gerbil">
|
||||
<ResponseField name="base_endpoint" type="string" required>
|
||||
Domain name included in WireGuard configuration for tunnel connections.
|
||||
|
||||
**Example**: `pangolin.example.com`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="start_port" type="integer">
|
||||
Starting port for WireGuard tunnels.
|
||||
|
||||
**Example**: `51820`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="use_subdomain" type="boolean">
|
||||
Whether to assign unique subdomains to Gerbil exit nodes.
|
||||
|
||||
**Default**: `false`
|
||||
|
||||
<Warning>
|
||||
Keep this set to `false` for most deployments.
|
||||
</Warning>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="subnet_group" type="string">
|
||||
IP address CIDR range for Gerbil exit node subnets.
|
||||
|
||||
**Example**: `10.0.0.0/8`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="block_size" type="integer">
|
||||
Block size for Gerbil exit node CIDR ranges.
|
||||
|
||||
**Example**: `24`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="site_block_size" type="integer">
|
||||
Block size for site CIDR ranges connected to Gerbil.
|
||||
|
||||
**Example**: `26`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
<ResponseField name="rate_limits" type="object">
|
||||
Rate limiting configuration for API requests.
|
||||
|
||||
<Expandable title="Rate Limits">
|
||||
<ResponseField name="global" type="object">
|
||||
Global rate limit settings for all external API requests.
|
||||
|
||||
<Expandable title="Global">
|
||||
<ResponseField name="window_minutes" type="integer">
|
||||
Time window for rate limiting in minutes.
|
||||
|
||||
**Example**: `1`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="max_requests" type="integer">
|
||||
Maximum number of requests allowed in the time window.
|
||||
|
||||
**Example**: `100`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Email Configuration
|
||||
|
||||
<ResponseField name="email" type="object">
|
||||
SMTP settings for sending transactional emails.
|
||||
|
||||
<Expandable title="Email">
|
||||
<ResponseField name="smtp_host" type="string">
|
||||
SMTP server hostname.
|
||||
|
||||
**Example**: `smtp.gmail.com`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="smtp_port" type="integer">
|
||||
SMTP server port.
|
||||
|
||||
**Example**: `587` (TLS) or `465` (SSL)
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="smtp_user" type="string">
|
||||
SMTP username.
|
||||
|
||||
**Example**: `no-reply@example.com`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="smtp_pass" type="string">
|
||||
SMTP password.
|
||||
|
||||
**Environment Variable**: `EMAIL_SMTP_PASS`
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="smtp_secure" type="boolean">
|
||||
Whether to use secure connection (SSL/TLS).
|
||||
|
||||
**Default**: `false`
|
||||
|
||||
<Tip>
|
||||
Enable this when using port 465 (SSL).
|
||||
</Tip>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="no_reply" type="string">
|
||||
From address for sent emails.
|
||||
|
||||
**Example**: `no-reply@example.com`
|
||||
|
||||
<Note>
|
||||
Usually the same as `smtp_user`.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="smtp_tls_reject_unauthorized" type="boolean">
|
||||
Whether to fail on invalid server certificates.
|
||||
|
||||
**Default**: `true`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Feature Flags
|
||||
|
||||
<ResponseField name="flags" type="object">
|
||||
Feature flags to control application behavior.
|
||||
|
||||
<Expandable title="Flags">
|
||||
<ResponseField name="require_email_verification" type="boolean">
|
||||
Whether to require email verification for new users.
|
||||
|
||||
**Default**: `false`
|
||||
|
||||
<Warning>
|
||||
Only enable this if you have email configuration set up.
|
||||
</Warning>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="disable_signup_without_invite" type="boolean">
|
||||
Whether to disable public user registration.
|
||||
|
||||
**Default**: `false`
|
||||
|
||||
<Note>
|
||||
Users can still sign up with valid invites when enabled.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="disable_user_create_org" type="boolean">
|
||||
Whether to prevent users from creating organizations.
|
||||
|
||||
**Default**: `false`
|
||||
|
||||
<Note>
|
||||
Server admins can always create organizations.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="allow_base_domain_resources" type="boolean">
|
||||
Whether to allow resources on base domains.
|
||||
|
||||
**Default**: `true`
|
||||
|
||||
<Note>
|
||||
When disabled, only subdomain resources are allowed.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="enable_integration_api" type="boolean">
|
||||
Whether to enable the integration API.
|
||||
|
||||
**Default**: `false`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Database Configuration
|
||||
|
||||
<ResponseField name="postgres" type="object">
|
||||
PostgreSQL database configuration (optional).
|
||||
|
||||
<Expandable title="PostgreSQL">
|
||||
<ResponseField name="connection_string" type="string" required>
|
||||
PostgreSQL connection string.
|
||||
|
||||
**Example**: `postgresql://user:password@host:port/database`
|
||||
|
||||
<Note>
|
||||
See [PostgreSQL documentation](../database/postgres) for setup instructions.
|
||||
</Note>
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Some configuration values can be set using environment variables for enhanced security:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Server Secret" icon="key">
|
||||
**Variable**: `SERVER_SECRET`
|
||||
|
||||
**Config**: `server.secret`
|
||||
|
||||
Use this to avoid hardcoding secrets in your config file.
|
||||
</Card>
|
||||
|
||||
<Card title="Email Password" icon="mail">
|
||||
**Variable**: `EMAIL_SMTP_PASS`
|
||||
|
||||
**Config**: `email.smtp_pass`
|
||||
|
||||
Keep SMTP passwords secure using environment variables.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
34
self-host/advanced/container-cli-tool.mdx
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: "Internal CLI (pangctl)"
|
||||
description: "Command-line tool for managing your Pangolin instance"
|
||||
---
|
||||
|
||||
The Pangolin container includes a CLI tool called `pangctl` that provides commands to help you manage your Pangolin instance.
|
||||
|
||||
## Accessing the CLI
|
||||
|
||||
Run the following command on the host where the Pangolin container is running:
|
||||
|
||||
```bash
|
||||
docker exec -it pangolin pangctl <command>
|
||||
```
|
||||
|
||||
## Available Commands
|
||||
|
||||
To see all available commands:
|
||||
|
||||
```bash
|
||||
docker exec -it pangolin pangctl --help
|
||||
```
|
||||
|
||||
## Set Admin Credentials
|
||||
|
||||
Set or reset admin credentials for your Pangolin instance:
|
||||
|
||||
```bash
|
||||
docker exec -it pangolin pangctl set-admin-credentials --email "admin@example.com" --password "Password123!"
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Use a strong password and keep your admin credentials secure.
|
||||
</Warning>
|
||||
100
self-host/advanced/database-options.mdx
Normal file
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: "Database Options"
|
||||
description: "Configure SQLite or PostgreSQL database for Pangolin"
|
||||
---
|
||||
|
||||
Pangolin supports two database options: SQLite for simplicity and PostgreSQL for production deployments.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="SQLite (Default)" icon="database">
|
||||
- No configuration required
|
||||
- Easy to use and portable
|
||||
- Built into the main image
|
||||
- Perfect for development
|
||||
</Card>
|
||||
|
||||
<Card title="PostgreSQL" icon="database">
|
||||
- Production-ready database
|
||||
- Better performance at scale
|
||||
- Requires separate image
|
||||
- Advanced configuration options
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## SQLite
|
||||
|
||||
By default, Pangolin uses SQLite for its ease of use and portability.
|
||||
|
||||
**Docker Image**: `fosrl/pangolin:<version>`
|
||||
|
||||
<Note>
|
||||
No configuration is required to use SQLite with Pangolin.
|
||||
</Note>
|
||||
|
||||
## PostgreSQL
|
||||
|
||||
You can optionally use PostgreSQL for production deployments.
|
||||
|
||||
**Docker Image**: `fosrl/pangolin:postgresql-<version>`
|
||||
|
||||
### Configuration
|
||||
|
||||
Add the following section to your Pangolin configuration file:
|
||||
|
||||
```yaml title="config.yml"
|
||||
postgres:
|
||||
connection_string: postgresql://<user>:<password>@<host>:<port>/<database>
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Replace the placeholders with your actual PostgreSQL connection details.
|
||||
</Warning>
|
||||
|
||||
### Docker Compose Example
|
||||
|
||||
This example sets up PostgreSQL with health checks to ensure the database is ready before Pangolin starts:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
name: pangolin
|
||||
services:
|
||||
pangolin:
|
||||
image: fosrl/pangolin:postgresql-latest # Don't use latest in production
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||
interval: "10s"
|
||||
timeout: "10s"
|
||||
retries: 15
|
||||
|
||||
# ... other services ...
|
||||
|
||||
postgres:
|
||||
image: postgres:17
|
||||
container_name: postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- ./config/postgres:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
```
|
||||
|
||||
<Warning>
|
||||
This example is not necessarily production-ready. Adjust the configuration according to your needs and security requirements.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
Do not use `latest` tags in production. Use specific version tags for stability.
|
||||
</Note>
|
||||
|
||||
71
self-host/advanced/integration-api.mdx
Normal file
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: "Enable Integration API"
|
||||
description: "Enable and configure the Integration API for external access"
|
||||
---
|
||||
|
||||
The Integration API provides programmatic access to Pangolin functionality. It includes OpenAPI documentation via Swagger UI.
|
||||
|
||||
## Enable Integration API
|
||||
|
||||
Update your Pangolin configuration file:
|
||||
|
||||
```yaml title="config.yml"
|
||||
flags:
|
||||
enable_integration_api: true
|
||||
```
|
||||
|
||||
If you want to specify a port other than the default `3003`, you can do so in the config as well:
|
||||
|
||||
```yaml title="config.yml"
|
||||
server:
|
||||
integration_port: 3003 # Specify different port
|
||||
```
|
||||
|
||||
## Configure Traefik Routing
|
||||
|
||||
Add the following configuration to your `dynamic_config.yml` to expose the Integration API at `https://api.example.com/v1`:
|
||||
|
||||
```yaml title="dynamic_config.yml"
|
||||
http:
|
||||
middlewares:
|
||||
redirect-to-https:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
|
||||
routers:
|
||||
int-api-router-redirect:
|
||||
rule: "Host(`api.example.com`)"
|
||||
service: int-api-service
|
||||
entryPoints:
|
||||
- web
|
||||
middlewares:
|
||||
- redirect-to-https
|
||||
|
||||
int-api-router:
|
||||
rule: "Host(`api.example.com`)"
|
||||
service: int-api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
services:
|
||||
int-api-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3003"
|
||||
```
|
||||
|
||||
## Access Documentation
|
||||
|
||||
Once configured, access the Swagger UI documentation at:
|
||||
|
||||
```
|
||||
https://api.example.com/v1/docs
|
||||
```
|
||||
|
||||
<img src="/images/swagger.png" alt="Swagger UI Preview"/>
|
||||
|
||||
<Note>
|
||||
The Integration API will be accessible at `https://api.example.com/v1` for external applications.
|
||||
</Note>
|
||||
101
self-host/advanced/postgresql.mdx
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: "Database Options"
|
||||
description: "Configure SQLite or PostgreSQL database for Pangolin"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
> Choose between SQLite (default) or PostgreSQL for your database
|
||||
|
||||
Pangolin supports two database options: SQLite for simplicity and PostgreSQL for production deployments.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="SQLite (Default)" icon="database">
|
||||
- No configuration required
|
||||
- Easy to use and portable
|
||||
- Built into the main image
|
||||
- Perfect for development
|
||||
</Card>
|
||||
|
||||
<Card title="PostgreSQL" icon="postgres">
|
||||
- Production-ready database
|
||||
- Better performance at scale
|
||||
- Requires separate image
|
||||
- Advanced configuration options
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## SQLite
|
||||
|
||||
By default, Pangolin uses SQLite for its ease of use and portability.
|
||||
|
||||
**Docker Image**: `fosrl/pangolin:<version>`
|
||||
|
||||
<Note>
|
||||
No configuration is required to use SQLite with Pangolin.
|
||||
</Note>
|
||||
|
||||
## PostgreSQL
|
||||
|
||||
You can optionally use PostgreSQL for production deployments.
|
||||
|
||||
**Docker Image**: `fosrl/pangolin:postgresql-<version>`
|
||||
|
||||
### Configuration
|
||||
|
||||
Add the following section to your Pangolin configuration file:
|
||||
|
||||
```yaml title="config.yml"
|
||||
postgres:
|
||||
connection_string: postgresql://<user>:<password>@<host>:<port>/<database>
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Replace the placeholders with your actual PostgreSQL connection details.
|
||||
</Warning>
|
||||
|
||||
### Docker Compose Example
|
||||
|
||||
This example sets up PostgreSQL with health checks to ensure the database is ready before Pangolin starts:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
name: pangolin
|
||||
services:
|
||||
pangolin:
|
||||
image: fosrl/pangolin:postgresql-latest
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||
interval: "10s"
|
||||
timeout: "10s"
|
||||
retries: 15
|
||||
|
||||
postgres:
|
||||
image: postgres:17
|
||||
container_name: postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- ./config/postgres:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
```
|
||||
|
||||
<Warning>
|
||||
This example is not necessarily production-ready. Adjust the configuration according to your needs and security requirements.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
Do not use `latest` tags in production. Use specific version tags for stability.
|
||||
</Note>
|
||||
249
self-host/advanced/wild-card-domains.mdx
Normal file
@@ -0,0 +1,249 @@
|
||||
---
|
||||
title: "Wildcard Domains"
|
||||
description: "Configure wildcard SSL certificates for automatic subdomain security with DNS-01 challenge"
|
||||
---
|
||||
|
||||
Wildcard certificates allow you to secure unlimited subdomains with a single SSL certificate, eliminating the need to generate individual certificates for each subdomain. Pangolin uses Traefik's built-in Let's Encrypt integration to automatically manage these certificates.
|
||||
|
||||
<Warning>
|
||||
Before setting up wildcard certificates, you must have a domain that you own and control. You must also have access to the DNS records for this domain.
|
||||
</Warning>
|
||||
|
||||
<Info>
|
||||
Since Pangolin uses Traefik as a reverse proxy, it has built-in support for Let's Encrypt certificates. This allows you to easily secure your Pangolin instance and all proxied resources with HTTPS. Let's Encrypt provides free SSL certificates, which are automatically renewed.
|
||||
</Info>
|
||||
|
||||
If you used the default settings during installation, your Traefik instance should be set up to use `HTTP-01` challenge for certificate generation. This challenge is the easiest to configure and requires that the Traefik instance be accessible from the internet on port 80.
|
||||
|
||||
<Note>
|
||||
It is highly recommended that you read the [official Traefik documentation](https://doc.traefik.io/traefik/https/acme/) on ACME and Let's Encrypt before proceeding.
|
||||
</Note>
|
||||
|
||||
## Benefits of Wildcard Certificates
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Single Certificate" icon="certificate">
|
||||
Secure unlimited subdomains with one certificate, reducing management overhead.
|
||||
</Card>
|
||||
|
||||
<Card title="Instant Subdomains" icon="bolt">
|
||||
Add new subdomains without waiting for certificate generation (up to a few minutes).
|
||||
</Card>
|
||||
|
||||
<Card title="Rate Limit Friendly" icon="shield">
|
||||
Reduce Let's Encrypt rate limit impact by using fewer certificate requests.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### Examples
|
||||
|
||||
- A wildcard cert `*.example.com` could protect:
|
||||
- `api.example.com`
|
||||
- `blog.example.com`
|
||||
- `dashboard.example.com`
|
||||
- Another wildcard `*.subdomain.example.com` could protect:
|
||||
- `api.subdomain.example.com`
|
||||
- `blog.subdomain.example.com`
|
||||
|
||||
<Info>
|
||||
The [rate limits](https://letsencrypt.org/docs/rate-limits/) for Let's Encrypt are per domain. Using a wildcard certificate reduces the number of domains you have, which can help you avoid hitting these limits.
|
||||
</Info>
|
||||
|
||||
## Setting Up Wildcard Certificates
|
||||
|
||||
<Steps>
|
||||
<Step title="Stop the stack">
|
||||
Make sure the stack is not running before making configuration changes.
|
||||
</Step>
|
||||
|
||||
<Step title="Update Traefik configuration">
|
||||
Update the Traefik configuration to use the DNS-01 challenge instead of the HTTP-01 challenge. This tells Traefik to use your DNS provider to create the DNS records needed for the challenge.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Pangolin">
|
||||
Set the `prefer_wildcard_cert` flag to `true` in the Pangolin configuration file for your domain.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Note>
|
||||
This setting will try to encourage Traefik to request one wildcard certificate for each level of the domain used by your existing resources.
|
||||
|
||||
**Example**: If you have two resources `blog.example.com` and `blog.subdomain.example.com`, Traefik should try to request a wildcard certificate for `*.example.com` and `*.subdomain.example.com` automatically for you.
|
||||
</Note>
|
||||
|
||||
## Traefik Configuration
|
||||
|
||||
### Default Config for HTTP-01 Challenge
|
||||
|
||||
This is the default config generated by the installer. This is shown here for reference to compare with the wildcard config below.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="1. HTTP Challenge Configuration">
|
||||
Tell Traefik to use the `web` entrypoint for the HTTP challenge.
|
||||
|
||||
```yaml title="traefik_config.yml" highlight={4,5}
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
email: admin@example.com
|
||||
storage: "/letsencrypt/acme.json"
|
||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="2. Dynamic Configuration">
|
||||
Set the cert resolver to `letsencrypt` and the entrypoint to `websecure` in the dynamic config.
|
||||
|
||||
```yaml title="dynamic_config.yml"
|
||||
next-router:
|
||||
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)"
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Wildcard Config for DNS-01 Challenge
|
||||
|
||||
<Steps>
|
||||
<Step title="1. Configure DNS Challenge">
|
||||
Tell Traefik to use your DNS provider for the DNS challenge. In this example, we are using Cloudflare.
|
||||
|
||||
```yaml title="traefik_config.yml" highlight={4,5}
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
dnsChallenge:
|
||||
provider: "cloudflare" # your DNS provider
|
||||
# see https://doc.traefik.io/traefik/https/acme/#providers
|
||||
email: "admin@example.com"
|
||||
storage: "/letsencrypt/acme.json"
|
||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="2. Add Wildcard Domains">
|
||||
Add the domain and wildcard domain to the domains section of the next (front end) router in the dynamic config. This tells Traefik to generate a wildcard certificate for the base domain and all subdomains.
|
||||
|
||||
```yaml title="dynamic_config.yml" highlight={9-12}
|
||||
next-router:
|
||||
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)"
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
domains:
|
||||
- main: "example.com"
|
||||
sans:
|
||||
- "*.example.com"
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="3. Add Environment Variables">
|
||||
Add the environment variables for your DNS provider to the Traefik service in the docker compose file. This allows Traefik to authenticate with your DNS provider to create the DNS records needed for the challenge.
|
||||
|
||||
```yaml title="docker-compose.yml" highlight={11-13}
|
||||
traefik:
|
||||
image: traefik:v3.4.0
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
network_mode: service:gerbil
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --configFile=/etc/traefik/traefik_config.yml
|
||||
# Add the environment variables for your DNS provider.
|
||||
environment:
|
||||
CLOUDFLARE_DNS_API_TOKEN: "your-cloudflare-api-token"
|
||||
volumes:
|
||||
- ./config/traefik:/etc/traefik:ro
|
||||
- ./config/letsencrypt:/letsencrypt
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Warning>
|
||||
If you're using Cloudflare, make sure your API token has the permissions Zone/Zone/Read and Zone/DNS/Edit and make sure it applies to all zones.
|
||||
</Warning>
|
||||
|
||||
<Info>
|
||||
Traefik supports most DNS providers. You can find a full list of supported providers and how to configure them in the [Traefik documentation on providers](https://doc.traefik.io/traefik/https/acme/#providers).
|
||||
</Info>
|
||||
|
||||
## Verify it Works
|
||||
|
||||
<Tips>
|
||||
<Tip title="Clear Old Certificates">
|
||||
You can ensure Traefik doesn't try to use the old certs by deleting the previously used `acme.json` file. This will force Traefik to generate a new certificate on the next start.
|
||||
</Tip>
|
||||
</Tips>
|
||||
|
||||
<Steps>
|
||||
<Step title="Start the stack">
|
||||
Start the stack and watch the logs. You should notice that Traefik is making calls to your DNS provider to create the necessary records to complete the challenge.
|
||||
</Step>
|
||||
|
||||
<Step title="Check logs">
|
||||
For debugging purposes, you may find it useful to set the log level of Traefik to `debug` in the `traefik_config.yml` file.
|
||||
</Step>
|
||||
|
||||
<Step title="Test new resource">
|
||||
After Traefik is done waiting for the cert to verify, try to create a new resource with an unused subdomain. Traefik should not try to generate a new certificate, but instead use the wildcard certificate. The domain should also be secured immediately instead of waiting for a new certificate to be generated.
|
||||
</Step>
|
||||
|
||||
<Step title="Verify certificate">
|
||||
You can also check the volume (in the example above at `config/letsencrypt/`) for the correct certificates. In the `acme.json` file you should see something similar to the following. Note the `*.` in the domain.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
```json highlight={5}
|
||||
{
|
||||
"Certificates": [
|
||||
{
|
||||
"domain": {
|
||||
"main": "*.example.com"
|
||||
},
|
||||
"certificate": "...",
|
||||
"key": "...",
|
||||
"Store": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Certificate not generating">
|
||||
**Problem**: Wildcard certificate not being created.
|
||||
|
||||
**Solutions**:
|
||||
- Verify DNS provider credentials are correct
|
||||
- Check that API token has proper permissions
|
||||
- Ensure domain ownership and DNS access
|
||||
- Review Traefik logs for specific error messages
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="DNS challenge failing">
|
||||
**Problem**: DNS-01 challenge not completing.
|
||||
|
||||
**Solutions**:
|
||||
- Verify DNS provider is supported by Traefik
|
||||
- Check API token permissions and scope
|
||||
- Ensure DNS propagation has completed
|
||||
- Review provider-specific configuration
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Old certificates still being used">
|
||||
**Problem**: Traefik using old HTTP-01 certificates.
|
||||
|
||||
**Solution**: Delete the `acme.json` file to force new certificate generation.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
30
self-host/advanced/without-tunneling.mdx
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: "Without Tunneling"
|
||||
description: "Use Pangolin as a local reverse proxy without Gerbil tunneling"
|
||||
---
|
||||
|
||||
Use Pangolin as a local reverse proxy and authentication manager
|
||||
|
||||
You can use Pangolin without Gerbil and tunneling. In this configuration, Pangolin acts as a normal reverse proxy and authentication manager that can be deployed on your local network to provide access to resources.
|
||||
|
||||
<Note>
|
||||
You can also use "local" sites to expose resources on the same VPS as Pangolin in addition to remote sites.
|
||||
</Note>
|
||||
|
||||
## Setup
|
||||
|
||||
### Using the Installer
|
||||
|
||||
When asked if you want to install Gerbil for tunneling, select **No**. Gerbil will be removed from the Docker Compose configuration.
|
||||
|
||||
### Manual Installation
|
||||
|
||||
Follow the [manual install steps](/self-host/manual/docker-compose), but **Gerbil is not required**. Your Docker Compose should not include the Gerbil container.
|
||||
|
||||
## How It Works
|
||||
|
||||
When Gerbil starts up, it registers itself with Pangolin. By not installing Gerbil, you will only have the option to choose the "Local" connection method. This means Traefik will use the local network to reach your resources.
|
||||
|
||||
<Warning>
|
||||
All setup remains the same, except Pangolin and Traefik must now be on the same network as the resources you want to proxy to.
|
||||
</Warning>
|
||||
113
self-host/choosing-a-vps.mdx
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: "Choosing a VPS"
|
||||
description: "Compare hosting options and find the best VPS for your Pangolin deployment"
|
||||
---
|
||||
|
||||
Pangolin generally requires minimal resources to run effectively. A basic VPS with **1 vCPU, 1GB RAM, and 8GB SSD** is sufficient for most deployments.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Minimum Requirements">
|
||||
- **CPU**: 1 vCPU
|
||||
- **RAM**: 1GB
|
||||
- **Storage**: 8GB SSD
|
||||
- **Bandwidth**: 1TB/month
|
||||
</Card>
|
||||
|
||||
<Card title="Recommended Specs">
|
||||
- **CPU**: 2 vCPU
|
||||
- **RAM**: 2GB
|
||||
- **Storage**: 20GB SSD
|
||||
- **Bandwidth**: 2TB/month
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Recommended Options
|
||||
|
||||
<Tip>
|
||||
We're part of RackNerd's affiliate program. Using our links helps support Pangolin development at no extra cost to you.
|
||||
</Tip>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="RackNerd Starter Plan" href="https://my.racknerd.com/aff.php?aff=13788&pid=912">
|
||||
**$0.91/month** - Perfect for personal use and small deployments
|
||||
|
||||
- 1 vCPU, 1GB RAM, 20GB SSD
|
||||
- Excellent performance for the price
|
||||
- Reliable uptime and support
|
||||
</Card>
|
||||
|
||||
<Card title="RackNerd Basic Plan" href="https://my.racknerd.com/aff.php?aff=13788&pid=913">
|
||||
**$1.47/month** - Ideal for small teams and growing deployments
|
||||
|
||||
- 2 vCPU, 2GB RAM, 30GB SSD
|
||||
- Great value for performance
|
||||
</Card>
|
||||
|
||||
<Card title="RackNerd Standard Plan" href="https://my.racknerd.com/aff.php?aff=13788&pid=914">
|
||||
**$2.49/month** - Best for larger teams and production use
|
||||
|
||||
- 3 vCPU, 3.5GB RAM, 60GB SSD
|
||||
- Room for growth and scaling
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## VPS Provider Comparison
|
||||
|
||||
| Provider | Plan | CPU | RAM | Storage | Price/Month | Price/Year | Best For |
|
||||
|----------|------|-----|-----|---------|-------------|------------|----------|
|
||||
| **[RackNerd](https://my.racknerd.com/aff.php?aff=13788)** | Starter | 1 vCPU | 1GB | 20GB SSD | $0.91 | $10.96 | Budget users |
|
||||
| **[RackNerd](https://my.racknerd.com/aff.php?aff=13788)** | Basic | 2 vCPU | 2GB | 30GB SSD | $1.47 | $17.66 | Small teams |
|
||||
| **[RackNerd](https://my.racknerd.com/aff.php?aff=13788)** | Standard | 3 vCPU | 3.5GB | 60GB SSD | $2.49 | $29.89 | Growing teams |
|
||||
| **[Hetzner Cloud](https://www.hetzner.com/cloud)** | CX11 | 2 vCPU | 4GB | 40GB SSD | $4.59 | $55.08 | Performance |
|
||||
| **[UpCloud](https://upcloud.com/pricing/)** | 1xCPU | 1 vCPU | 1GB | 10GB SSD | $3.30 | $39.60 | European users |
|
||||
| **[Vultr](https://www.vultr.com/pricing)** | Cloud Compute | 1 vCPU | 1GB | 25GB SSD | $5.00 | $60.00 | Global presence |
|
||||
| **[Linode](https://www.linode.com/pricing/)** | Nanode | 1 vCPU | 1GB | 25GB SSD | $5.00 | $60.00 | Developer friendly |
|
||||
| **[DigitalOcean](https://www.digitalocean.com/pricing/droplets)** | Basic | 1 vCPU | 1GB | 25GB SSD | $6.00 | $72.00 | Easy setup |
|
||||
| **[OVHcloud](https://www.ovhcloud.com/en/vps/)** | Starter | 2 vCPU | 2GB | 40GB SSD | $5.50 | $66.00 | European users |
|
||||
| **[AWS EC2](https://instances.vantage.sh/)** | t3.micro | 2 vCPU | 1GB | 8GB SSD | $8.50 | $102.00 | Enterprise |
|
||||
|
||||
<Info>
|
||||
Prices shown are approximate and may vary based on location, promotions, and currency exchange rates. Check provider websites for current pricing.
|
||||
</Info>
|
||||
|
||||
## Selection Criteria
|
||||
|
||||
When choosing your VPS provider, consider these factors:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Performance Requirements">
|
||||
**Resource usage depends on several key factors:**
|
||||
|
||||
**Primary factors:**
|
||||
- **Number of connected sites**: More sites = higher CPU and memory usage
|
||||
- **Data throughput**: Amount of traffic transiting through the server
|
||||
|
||||
**Secondary factors:**
|
||||
- **Dashboard UI usage**: Active admin sessions and configuration changes
|
||||
- **Database activity**: User management, logging, and analytics queries
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Geographic Location">
|
||||
**Choose a data center close to your users:**
|
||||
- **North America**: RackNerd, DigitalOcean, Vultr
|
||||
- **Europe**: Hetzner, OVHcloud, UpCloud
|
||||
- **Asia Pacific**: Vultr, Linode, DigitalOcean
|
||||
- **Global**: AWS, Google Cloud, Azure
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Support and Reliability">
|
||||
**Consider these factors:**
|
||||
- **Uptime guarantees**: Most providers offer 99.9%+
|
||||
- **Support quality**: 24/7 support vs. community forums
|
||||
- **Backup options**: Automated backups vs. manual
|
||||
- **Monitoring**: Built-in monitoring tools
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Cost Considerations">
|
||||
**Hidden costs to watch for:**
|
||||
- **Bandwidth overages**: Most plans include 1-2TB/month
|
||||
- **Backup storage**: Additional charges for automated backups
|
||||
- **IPv4 addresses**: Some providers charge extra
|
||||
- **Support tiers**: Premium support may cost extra
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
201
self-host/dns-and-networking.mdx
Normal file
@@ -0,0 +1,201 @@
|
||||
---
|
||||
title: "DNS & Networking"
|
||||
description: "Configure your domain, DNS records, and network settings for Pangolin deployment"
|
||||
---
|
||||
|
||||
Pangolin requires proper DNS configuration and network setup to function correctly. This guide covers domain setup, DNS records, port configuration, and networking considerations.
|
||||
|
||||
## DNS Configuration
|
||||
|
||||
### Basic DNS Records
|
||||
|
||||
You'll need to create A (or AAAA for IPv6) records pointing to your VPS IP address.
|
||||
|
||||
<Steps>
|
||||
<Step title="Create wildcard record">
|
||||
Create a wildcard subdomain record for your domain:
|
||||
|
||||
```
|
||||
Type: A
|
||||
Name: *
|
||||
Value: YOUR_VPS_IP_ADDRESS
|
||||
TTL: 300 (or default)
|
||||
```
|
||||
|
||||
<Check>
|
||||
This allows any subdomain (e.g., `app.example.com`, `api.example.com`) to resolve to your VPS.
|
||||
</Check>
|
||||
</Step>
|
||||
|
||||
<Step title="Create root domain record (optional)">
|
||||
If you plan to use your root domain as a resource:
|
||||
|
||||
```
|
||||
Type: A
|
||||
Name: @ (or leave blank)
|
||||
Value: YOUR_VPS_IP_ADDRESS
|
||||
TTL: 300 (or default)
|
||||
```
|
||||
|
||||
<Info>
|
||||
This is only needed if you want to use `example.com` (not just subdomains) as a resource.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Wait for propagation">
|
||||
DNS changes can take 5 minutes to 48 hours to propagate globally.
|
||||
|
||||
<Tip>
|
||||
Use Google DNS (8.8.8.8) or your provider's DNS to test changes faster.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Port Configuration
|
||||
|
||||
### Required Ports
|
||||
|
||||
Pangolin requires these ports to be open on your VPS:
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="TCP Port 80">
|
||||
**HTTP/SSL Verification**
|
||||
|
||||
- Let's Encrypt domain validation
|
||||
- Non-SSL resources
|
||||
- Can be disabled with wildcard certs
|
||||
</Card>
|
||||
|
||||
<Card title="TCP Port 443">
|
||||
**HTTPS Traffic**
|
||||
|
||||
- Pangolin web dashboard
|
||||
- SSL-secured resources
|
||||
- Essential for operation
|
||||
</Card>
|
||||
|
||||
<Card title="UDP Port 51820">
|
||||
**WireGuard Tunnel**
|
||||
|
||||
- Newt client connections
|
||||
- Gerbil tunnel endpoint
|
||||
- Secure traffic routing
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### Docker Port Exposure
|
||||
|
||||
By default, Pangolin exposes these ports on all interfaces:
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
ports:
|
||||
- "80:80" # HTTP/SSL verification and non-SSL resources
|
||||
- "443:443" # HTTPS for web UI and SSL resources
|
||||
- "51820:51820" # WireGuard for Newt and client connections
|
||||
```
|
||||
|
||||
### Firewall Configuration
|
||||
|
||||
Ensure your VPS firewall allows these ports:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Cloud Provider">
|
||||
Configure security groups/firewall rules in your cloud provider's dashboard to allow:
|
||||
|
||||
- TCP ports 80 and 443
|
||||
- UDP port 51820
|
||||
</Tab>
|
||||
|
||||
<Tab title="UFW (Ubuntu)">
|
||||
```bash
|
||||
sudo ufw allow 80/tcp
|
||||
sudo ufw allow 443/tcp
|
||||
sudo ufw allow 51820/udp
|
||||
sudo ufw enable
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="firewalld (CentOS/RHEL)">
|
||||
```bash
|
||||
sudo firewall-cmd --permanent --add-port=80/tcp
|
||||
sudo firewall-cmd --permanent --add-port=443/tcp
|
||||
sudo firewall-cmd --permanent --add-port=51820/udp
|
||||
sudo firewall-cmd --reload
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Internal Network Configuration
|
||||
|
||||
### Default Subnet Settings
|
||||
|
||||
Pangolin uses these default network settings:
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
block_size: 24
|
||||
site_block_size: 30
|
||||
subnet_group: 100.89.137.0/20
|
||||
```
|
||||
|
||||
**What this means:**
|
||||
- **Gerbil network**: Uses first /24 subnet in `100.89.137.0/20` range
|
||||
- **Site allocation**: Each site gets a /30 subnet (4 IPs)
|
||||
- **CGNAT range**: Avoids conflicts with most private networks
|
||||
|
||||
<Info>
|
||||
The `100.89.137.0/20` range is in the CGNAT (Carrier-Grade NAT) space, which should avoid conflicts with typical private networks (192.168.x.x, 10.x.x.x, 172.16-31.x.x).
|
||||
</Info>
|
||||
|
||||
<Warning>
|
||||
**Important**: If this subnet conflicts with your network, change it in your config **before** registering your first Gerbil.
|
||||
</Warning>
|
||||
|
||||
### Customizing Network Settings
|
||||
|
||||
If you need to change the default network:
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
block_size: 24 # Size of Gerbil's network block
|
||||
site_block_size: 30 # Size of each site's network block
|
||||
subnet_group: 10.0.0.0/8 # Custom subnet range
|
||||
start_port: 51820 # WireGuard server port
|
||||
```
|
||||
|
||||
<Tip>
|
||||
For heavy WireGuard usage, consider increasing `site_block_size` to 29 (8 IPs) or 28 (16 IPs) per site.
|
||||
</Tip>
|
||||
|
||||
## Docker Networking
|
||||
|
||||
### Local Services
|
||||
|
||||
When deploying services in Docker alongside Pangolin:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Container Communication">
|
||||
**For services in the same Docker Compose:**
|
||||
|
||||
- Use service names as hostnames
|
||||
- Example: `http://pangolin:8080`
|
||||
- Docker Compose creates internal network automatically
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Host Machine Access">
|
||||
**To access services on the host machine:**
|
||||
|
||||
- Use `172.17.0.1` (Docker bridge gateway)
|
||||
- Or use `host.docker.internal` (Docker Desktop)
|
||||
- Example: `http://172.17.0.1:3000`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="External Services">
|
||||
**For services outside Docker:**
|
||||
|
||||
- Use the host's public IP address
|
||||
- Ensure firewall allows the required ports
|
||||
- Consider using VPN or secure tunnels
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
100
self-host/how-to-update.mdx
Normal file
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: "How to Update"
|
||||
description: "Keep your Pangolin deployment up to date with the latest features and security patches"
|
||||
---
|
||||
|
||||
Updating Pangolin is straightforward since it's a collection of Docker images. Simply pull the latest images and restart the stack. Migration scripts run automatically to update your database and configuration files when needed.
|
||||
|
||||
## Before You Update
|
||||
|
||||
<Warning>
|
||||
**Always backup your data before updating.** Copy your `config` directory to a safe location so you can roll back if needed.
|
||||
</Warning>
|
||||
|
||||
<Tip>
|
||||
**Recommended**: Update incrementally between major versions. For example, update from 1.0.0 → 1.1.0 → 1.2.0 instead of jumping directly from 1.0.0 → 1.2.0.
|
||||
</Tip>
|
||||
|
||||
## Update Process
|
||||
|
||||
<Steps>
|
||||
<Step title="Stop the stack">
|
||||
Stop all running containers:
|
||||
|
||||
```bash
|
||||
sudo docker compose down
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Check latest versions">
|
||||
Find the latest version numbers:
|
||||
|
||||
- **Pangolin**: [GitHub Releases](https://github.com/fosrl/pangolin/releases)
|
||||
- **Gerbil**: [GitHub Releases](https://github.com/fosrl/gerbil/releases)
|
||||
- **Traefik**: [Docker Hub](https://hub.docker.com/_/traefik)
|
||||
|
||||
<Info>
|
||||
Look for the latest stable release (not pre-release or beta versions).
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Update version numbers">
|
||||
Edit your `docker-compose.yml` file and update the image versions:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
pangolin:
|
||||
image: fosrl/pangolin:1.7.3 # Update to latest version
|
||||
# ... rest of config
|
||||
|
||||
gerbil:
|
||||
image: fosrl/gerbil:1.2.1 # Update to latest version
|
||||
# ... rest of config
|
||||
|
||||
traefik:
|
||||
image: traefik:v3.4.0 # Update if needed
|
||||
# ... rest of config
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Update each service you want to upgrade. You can update them individually or all at once.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Pull new images">
|
||||
Download the updated Docker images:
|
||||
|
||||
```bash
|
||||
sudo docker compose pull
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Start the stack">
|
||||
Start the updated containers:
|
||||
|
||||
```bash
|
||||
sudo docker compose up -d
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Monitor the update">
|
||||
Watch the logs to ensure everything starts correctly:
|
||||
|
||||
```bash
|
||||
sudo docker compose logs -f
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Verify functionality">
|
||||
Test that everything is working:
|
||||
|
||||
1. Access your Pangolin dashboard
|
||||
2. Check that all sites are accessible
|
||||
3. Verify tunnel connections (if using Gerbil)
|
||||
4. Test any custom configurations
|
||||
|
||||
<Check>
|
||||
If everything works, your update is complete!
|
||||
</Check>
|
||||
</Step>
|
||||
</Steps>
|
||||
308
self-host/manual/docker-compose.mdx
Normal file
@@ -0,0 +1,308 @@
|
||||
---
|
||||
title: "Docker Compose"
|
||||
description: "Deploy Pangolin manually using Docker Compose without the automated installer"
|
||||
---
|
||||
|
||||
This guide walks you through setting up Pangolin manually using Docker Compose without the automated installer. This approach gives you full control over the configuration and deployment process.
|
||||
|
||||
This guide assumes you already have a Linux server with Docker and Docker Compose installed. If you don't, please refer to the [official Docker documentation](https://docs.docker.com/get-docker/) for installation instructions. You must also have root access to the server.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Checkout the [quick install guide](self-host/quick-install) for more info regarding what is needed before you install Pangolin.
|
||||
|
||||
## File Structure
|
||||
|
||||
Create the following directory structure for your Pangolin deployment:
|
||||
|
||||
```
|
||||
.
|
||||
├── config/
|
||||
│ ├── config.yml (*)
|
||||
│ ├── db/
|
||||
│ │ └── db.sqlite
|
||||
│ ├── key
|
||||
│ ├── letsencrypt/
|
||||
│ │ └── acme.json
|
||||
│ ├── logs/
|
||||
│ └── traefik/
|
||||
│ ├── traefik_config.yml (*)
|
||||
│ └── dynamic_config.yml (*)
|
||||
└── docker-compose.yml (*)
|
||||
```
|
||||
|
||||
<Info>
|
||||
Files marked with `(*)` must be created manually. Volumes and other files are generated automatically by the services.
|
||||
</Info>
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Configuration Files">
|
||||
**`config/config.yml`**: Main Pangolin configuration file
|
||||
- Contains all Pangolin settings and options
|
||||
- See [Configuration Guide](/pangolin/configuration/config) for details
|
||||
|
||||
**`config/traefik/traefik_config.yml`**: Traefik static configuration
|
||||
- Global Traefik settings and entry points
|
||||
- SSL certificate resolver configuration
|
||||
|
||||
**`config/traefik/dynamic_config.yml`**: Traefik dynamic configuration
|
||||
- HTTP routers and services for Pangolin
|
||||
- Load balancer and middleware configuration
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Generated Files">
|
||||
**`config/db/db.sqlite`**: SQLite database file
|
||||
- Created automatically on first startup
|
||||
- Contains all Pangolin data and settings
|
||||
|
||||
**`config/key`**: Private key file
|
||||
- Generated by Gerbil service
|
||||
- Used for WireGuard tunnel encryption
|
||||
|
||||
**`config/letsencrypt/acme.json`**: SSL certificate storage
|
||||
- Managed by Traefik
|
||||
- Contains Let's Encrypt certificates
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Docker Files">
|
||||
**`docker-compose.yml`**: Service definitions
|
||||
- Defines Pangolin, Gerbil, and Traefik services
|
||||
- Network configuration and volume mounts
|
||||
- Health checks and dependencies
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Steps>
|
||||
<Step title="Create configuration directory">
|
||||
```bash
|
||||
mkdir -p config/traefik config/db config/letsencrypt config/logs
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create configuration files">
|
||||
Create the main configuration files (see below):
|
||||
|
||||
- `docker-compose.yml` (in project root)
|
||||
- `config/traefik/traefik_config.yml`
|
||||
- `config/traefik/dynamic_config.yml`
|
||||
- `config/config.yml`
|
||||
</Step>
|
||||
|
||||
<Step title="Update domain and email">
|
||||
Edit the configuration files to replace:
|
||||
|
||||
- `pangolin.example.com` with your actual domain
|
||||
- `admin@example.com` with your email address
|
||||
|
||||
<Warning>
|
||||
Ensure your domain DNS is properly configured to point to your server's IP address.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Starting the Stack
|
||||
|
||||
<Steps>
|
||||
<Step title="Start the services">
|
||||
```bash
|
||||
sudo docker compose up -d
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Monitor startup">
|
||||
```bash
|
||||
sudo docker compose logs -f
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Verify services">
|
||||
```bash
|
||||
sudo docker compose ps
|
||||
```
|
||||
|
||||
All services should show "Up" status after a few minutes.
|
||||
</Step>
|
||||
|
||||
<Step title="Access the dashboard">
|
||||
Navigate to `https://your-domain.com/auth/initial-setup` to complete the initial setup.
|
||||
|
||||
<Check>
|
||||
The dashboard should load with SSL certificate automatically configured.
|
||||
</Check>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Docker Compose Configuration
|
||||
|
||||
Create `docker-compose.yml` in your project root:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
pangolin:
|
||||
image: fosrl/pangolin:latest # https://github.com/fosrl/pangolin/releases
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||
interval: "3s"
|
||||
timeout: "3s"
|
||||
retries: 15
|
||||
|
||||
gerbil:
|
||||
image: fosrl/gerbil:latest # https://github.com/fosrl/gerbil/releases
|
||||
container_name: gerbil
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --reachableAt=http://gerbil:3003
|
||||
- --generateAndSaveKeyTo=/var/config/key
|
||||
- --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config
|
||||
- --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth
|
||||
volumes:
|
||||
- ./config/:/var/config
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
ports:
|
||||
- 51820:51820/udp
|
||||
- 443:443 # Port for traefik because of the network_mode
|
||||
- 80:80 # Port for traefik because of the network_mode
|
||||
|
||||
traefik:
|
||||
image: traefik:v3.4.0
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
network_mode: service:gerbil # Ports appear on the gerbil service
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --configFile=/etc/traefik/traefik_config.yml
|
||||
volumes:
|
||||
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
|
||||
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
name: pangolin
|
||||
```
|
||||
|
||||
## Traefik Static Configuration
|
||||
|
||||
Create `config/traefik/traefik_config.yml`:
|
||||
|
||||
```yaml title="config/traefik/traefik_config.yml"
|
||||
api:
|
||||
insecure: true
|
||||
dashboard: true
|
||||
|
||||
providers:
|
||||
http:
|
||||
endpoint: "http://pangolin:3001/api/v1/traefik-config"
|
||||
pollInterval: "5s"
|
||||
file:
|
||||
filename: "/etc/traefik/dynamic_config.yml"
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
badger:
|
||||
moduleName: "github.com/fosrl/badger"
|
||||
version: "latest"
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
format: "common"
|
||||
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
email: admin@example.com # REPLACE WITH YOUR EMAIL
|
||||
storage: "/letsencrypt/acme.json"
|
||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
http:
|
||||
tls:
|
||||
certResolver: "letsencrypt"
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
|
||||
## Traefik Dynamic Configuration
|
||||
|
||||
Create `config/traefik/dynamic_config.yml`:
|
||||
|
||||
```yaml title="config/traefik/dynamic_config.yml"
|
||||
http:
|
||||
middlewares:
|
||||
redirect-to-https:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
|
||||
routers:
|
||||
# HTTP to HTTPS redirect router
|
||||
main-app-router-redirect:
|
||||
rule: "Host(`pangolin.example.com`)" # REPLACE WITH YOUR DOMAIN
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- web
|
||||
middlewares:
|
||||
- redirect-to-https
|
||||
|
||||
# Next.js router (handles everything except API and WebSocket paths)
|
||||
next-router:
|
||||
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # REPLACE WITH YOUR DOMAIN
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
# API router (handles /api/v1 paths)
|
||||
api-router:
|
||||
rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # REPLACE WITH YOUR DOMAIN
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
# WebSocket router
|
||||
ws-router:
|
||||
rule: "Host(`pangolin.example.com`)" # REPLACE WITH YOUR DOMAIN
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
services:
|
||||
next-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3002" # Next.js server
|
||||
|
||||
api-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3000" # API/WebSocket server
|
||||
```
|
||||
|
||||
## Pangolin Configuration
|
||||
|
||||
Create `config/config.yml` with your Pangolin settings. See the [configuration guide](/self-host/advanced/config-file) for detailed options and examples.
|
||||
356
self-host/manual/unraid.mdx
Normal file
@@ -0,0 +1,356 @@
|
||||
---
|
||||
title: "Unraid Deployment"
|
||||
description: "Deploy Pangolin on Unraid for local reverse proxy and tunneling"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide explains how to use Pangolin and Traefik as a local reverse proxy without Gerbil and its tunneling features. The second (optional) part will expand on this and show how to enable tunneling by setting up Gerbil.
|
||||
|
||||
All containers are available in the Unraid Community Apps (CA) store. If you're not familiar with Unraid, you can find more information on their [website](https://unraid.net/).
|
||||
|
||||
This installation has a lot of moving parts and is a bit non-standard for Unraid because Pangolin and its components were designed to run as micro-services on a VPS in tunneling mode. However, some may want to use "Local" reverse proxying on their Unraid server or use their Unraid server as a tunnel controller with Gerbil. For either of these use cases, follow the steps outlined in this guide.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A working Unraid server.
|
||||
- A domain name with access to configure DNS and the ability to port forward on your network.
|
||||
- The networking is the same as for the VPS, just on your local network, so please refer to [networking page](/self-host/dns-and-networking) for more info.
|
||||
|
||||
## Create a Docker Network
|
||||
|
||||
Before starting, create a new docker network on Unraid. This will simplify things, and allow the containers to communicate with each other via their container names. If you already have a network, there is no need to create another one.
|
||||
|
||||
1. Open the web terminal in Unraid.
|
||||
2. Run the following command:
|
||||
|
||||
<Info>
|
||||
You can use any name you want for the network. We will use `mynetwork` in this guide.
|
||||
</Info>
|
||||
|
||||
```bash
|
||||
docker network create mynetwork
|
||||
```
|
||||
|
||||
For more info on this, see this [tutorial by IBRACORP](https://www.youtube.com/watch?v=7fzBDCI8O2w).
|
||||
|
||||
## 1. Setup Pangolin and Traefik
|
||||
|
||||
This first part will enable Pangolin to work in "Local" reverse proxy mode. Newt and WireGuard will **not** be able to be used after finishing this first part. However, if you want to use those features, you still need to follow this first part of the tutorial because we show how to set up Pangolin and Traefik first.
|
||||
|
||||
### Install and Setup Pangolin
|
||||
|
||||
#### 1. Create the Config Files
|
||||
|
||||
Pangolin uses a yaml file for configuration. If this is not present on start up, the container will throw an error and exit.
|
||||
|
||||
Create a `config.yml` file in the `config` folder.
|
||||
|
||||
See the [Configuration](/pangolin/configuration/config) section for what to put in this file.
|
||||
|
||||
```
|
||||
pangolin/
|
||||
├─ config/
|
||||
│ ├─ config.yml
|
||||
```
|
||||
|
||||
#### 2. Install Pangolin via the CA Store
|
||||
|
||||
#### 3. Configure Pangolin
|
||||
|
||||
Set the network to the one you created earlier.
|
||||
|
||||
<img
|
||||
src="/images/pangolin_config.png"
|
||||
alt="Pangolin configuration settings in Unraid"
|
||||
/>
|
||||
|
||||
**Ports:**
|
||||
|
||||
Due to the way Pangolin was designed to work with docker compose and a config file, the way it handles ports is a little different as compared to other popular Unraid containers. For all host ports:
|
||||
|
||||
The host ports, container ports, and ports in the config should match for simplicity. This is because the Pangolin config also has ports in it. If you decide to use a non-default port, you would need to edit the port in the template and the config file.
|
||||
|
||||
For example, to change the port for the WebUI:
|
||||
|
||||
- Click edit on the port
|
||||
- Set the "Container Port" to the new port you want to use
|
||||
- Set the "Host Port" to the new port you want to use
|
||||
- Edit Pangolin's config file and set `server.next_port` to the new port you want to use
|
||||
|
||||
#### 4. Start the Pangolin Container
|
||||
|
||||
<Warning>
|
||||
Pangolin will not start without a config file. If you have not created the config file or the config file is invalid, the container will throw an error and exit.
|
||||
</Warning>
|
||||
|
||||
#### 5. Log in to the dashboard
|
||||
|
||||
After successful installation:
|
||||
|
||||
1. Complete the initial admin user setup via the dashboard at `https://<your-domain>/auth/initial-setup`
|
||||
2. You can log in using the admin email and password you provided
|
||||
3. Create your first "Local" site for local reverse proxying
|
||||
|
||||
### Install and Setup Traefik
|
||||
|
||||
Before starting with Traefik, shut down the Pangolin container.
|
||||
|
||||
#### 1. Create the Config Files
|
||||
|
||||
Update the appdata path with new files for Traefik. At this point there may be some extra files generated by Pangolin.
|
||||
|
||||
```
|
||||
pangolin/
|
||||
├─ config/
|
||||
│ ├─ config.yml
|
||||
│ ├─ letsencrypt/
|
||||
│ ├─ traefik/
|
||||
│ │ ├─ dynamic_config.yml
|
||||
│ │ ├─ traefik_config.yml
|
||||
```
|
||||
|
||||
**`pangolin/config/traefik/traefik_config.yml`:**
|
||||
|
||||
```yaml title="pangolin/config/traefik/traefik_config.yml
|
||||
api:
|
||||
insecure: true
|
||||
dashboard: true
|
||||
|
||||
providers:
|
||||
http:
|
||||
endpoint: "http://pangolin:3001/api/v1/traefik-config"
|
||||
pollInterval: "5s"
|
||||
file:
|
||||
filename: "/etc/traefik/dynamic_config.yml"
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
badger:
|
||||
moduleName: "github.com/fosrl/badger"
|
||||
version: "latest"
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
format: "common"
|
||||
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
email: admin@example.com # REPLACE THIS WITH YOUR EMAIL
|
||||
storage: "/letsencrypt/acme.json"
|
||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
http:
|
||||
tls:
|
||||
certResolver: "letsencrypt"
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
|
||||
**`pangolin/config/traefik/dynamic_config.yml`:**
|
||||
|
||||
The dynamic configuration file is where you define the HTTP routers and services for the Pangolin frontend and backend. Below is an example configuration for a Next.js frontend and an API backend.
|
||||
|
||||
The domain you enter here is what will be used to access the main Pangolin dashboard. Make sure you have the DNS set up correctly for this domain. Point it to the IP address of the server running Pangolin.
|
||||
|
||||
```yaml title="pangolin/config/traefik/dynamic_config.yml"
|
||||
http:
|
||||
middlewares:
|
||||
redirect-to-https:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
|
||||
routers:
|
||||
# HTTP to HTTPS redirect router
|
||||
main-app-router-redirect:
|
||||
rule: "Host(`pangolin.example.com`)" # REPLACE THIS WITH YOUR DOMAIN
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- web
|
||||
middlewares:
|
||||
- redirect-to-https
|
||||
|
||||
# Next.js router (handles everything except API and WebSocket paths)
|
||||
next-router:
|
||||
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # REPLACE THIS WITH YOUR DOMAIN
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
# API router (handles /api/v1 paths)
|
||||
api-router:
|
||||
rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # REPLACE THIS WITH YOUR DOMAIN
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
# WebSocket router
|
||||
ws-router:
|
||||
rule: "Host(`pangolin.example.com`)" # REPLACE THIS WITH YOUR DOMAIN
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
services:
|
||||
next-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3002" # Next.js server
|
||||
|
||||
api-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3000" # API/WebSocket server
|
||||
```
|
||||
|
||||
#### 2. Install Traefik via the CA Store
|
||||
|
||||
This section will use the Traefik template from the "IBRACORP" repository. If you already have a Traefik installation running, you should manually configure your Traefik config to work with Pangolin.
|
||||
|
||||
<img
|
||||
src="/images/traefik_repo.png"
|
||||
width="400"
|
||||
alt="Traefik repository selection in Community Apps"
|
||||
/>
|
||||
|
||||
#### 3. Configure Traefik
|
||||
|
||||
<img
|
||||
src="/images/traefik_config.png"
|
||||
alt="Traefik configuration settings in Unraid"
|
||||
/>
|
||||
|
||||
<Info>
|
||||
Please refer to the official Traefik docs for more information on the Traefik configuration beyond this guide.
|
||||
</Info>
|
||||
|
||||
**Match your config to the one above. You will have to remove some of the default variables in the template that are not needed. You can always add them back if you need them later.**
|
||||
|
||||
**Network Type:**
|
||||
|
||||
Set the network type to the one you created earlier.
|
||||
|
||||
**Post Arguments:**
|
||||
|
||||
Tell Traefik where the config file is located by adding the following to the "Post Arguments" field. This is not the host path, but the path inside the container.
|
||||
|
||||
```bash
|
||||
--configFile=/etc/traefik/traefik_config.yml
|
||||
```
|
||||
|
||||
**Config Folder:**
|
||||
|
||||
If you're using the Traefik config generated by Pangolin, point this to the same appdata path as Pangolin, but append `/traefik`, like this: `<appdata>/config/traefik`.
|
||||
|
||||
**Lets Encrypt (Host Path 2 in screenshot):**
|
||||
|
||||
Traefik will store the certification information here. You can make this path anywhere you want. For simplicity, we're placing it in the same config path at `<appdata>/config/letsencrypt`.
|
||||
|
||||
**Ports:**
|
||||
|
||||
You will need to port forward the https and http ports listed in the config on your network's router.
|
||||
|
||||
#### 4. Port Forwarding
|
||||
|
||||
You will need to port forward the ports you set in the Traefik config on your network's router. This is so that Traefik can receive traffic from the internet. You should forward 443 to the https port and 80 to the http port you set in the Traefik config.
|
||||
|
||||
## 2. Add Gerbil for Tunneling (Optional)
|
||||
|
||||
<Info>
|
||||
If you do not want to use the tunneling feature of Pangolin and only want to use it as a local reverse proxy, you can stop here.
|
||||
</Info>
|
||||
|
||||
Before setting up Gerbil, shut down Traefik and Pangolin.
|
||||
|
||||
If you plan to use tunneling features of Pangolin with Newt or WireGuard, you will need to add Gerbil to the stack. Gerbil is the tunnel controller for Pangolin and is used to manage the tunnels between the Pangolin server and the client.
|
||||
|
||||
Luckily, adding Gerbil is fairly easy.
|
||||
|
||||
The important concept to understand going forward, is we need to network Traefik through Gerbil. All Traefik traffic goes through the Gerbil container and exits.
|
||||
|
||||
#### 1. Install Gerbil via the CA Store
|
||||
|
||||
#### 2. Configure Gerbil
|
||||
|
||||
Set the network to the one you created earlier.
|
||||
|
||||
<img
|
||||
src="/images/gerbil_config.png"
|
||||
alt="Gerbil configuration settings in Unraid"
|
||||
/>
|
||||
|
||||
**Important things to consider:**
|
||||
|
||||
**Internal Communication:**
|
||||
|
||||
Anywhere you see `http://pangolin:3001` must match. The hostname should be the name of the Pangolin container on the docker network you're using. This is because it is routed using the internal docker DNS address. The port must also match the port you have set for the internal port in Pangolin. These defaults will work unless you changed these values earlier when setting up Pangolin.
|
||||
|
||||
**WireGuard Port:**
|
||||
|
||||
The port you use for WireGuard must also match what you set the port to in the Pangolin config. By default we use a slightly different port than the standard WireGuard port to avoid conflicts with the built in WireGuard server in Unraid.
|
||||
|
||||
**HTTP and HTTPS Ports:**
|
||||
|
||||
You must open these ports because Traefik will be routed through Gerbil. These ports should match the ports you set in the Traefik config earlier. In the next step, we will set the network mode for Traefik which will close the ports on the Traefik side, and prevent conflicts. Before doing this, if you start the Traefik container at the same time as the Gerbil container with the same ports mapped to the host, you will get an error.
|
||||
|
||||
#### 3. Network Traefik Through Gerbil
|
||||
|
||||
As discussed earlier we need to network Traefik through Gerbil. This is pretty easy. We will do all of this in the Traefik container settings.
|
||||
|
||||
Toggle advanced settings, and add the following to the "Extra Parameters" section.
|
||||
|
||||
```bash
|
||||
--net=container:Gerbil
|
||||
```
|
||||
|
||||
Then, set "Network Type" to "None".
|
||||
|
||||
<img
|
||||
src="/images/traefik_networking.png"
|
||||
alt="Traefik networking configuration through Gerbil"
|
||||
/>
|
||||
|
||||
#### 4. Start the stack
|
||||
|
||||
We recommend to start the whole stack in the following order:
|
||||
|
||||
1. Pangolin
|
||||
2. Gerbil
|
||||
3. Traefik
|
||||
|
||||
#### 5. Port Forwarding
|
||||
|
||||
You will need to port forward the WireGuard port you set in the Gerbil config on your network's router. This is so that the client can connect to the server.
|
||||
|
||||
#### 6. Verify Tunnels are Functional
|
||||
|
||||
Your logs for Gerbil should look something like this:
|
||||
|
||||
<Info>
|
||||
You probably won't have the peer connection messages but in general, you should see the WireGuard interface being started.
|
||||
</Info>
|
||||
|
||||
<img
|
||||
src="/images/gerbil_logs.png"
|
||||
alt="Gerbil logs showing WireGuard interface startup"
|
||||
/>
|
||||
|
||||
Log back into the Pangolin dashboard and create a new site with Newt or basic WireGuard. Copy the credentials to your client and connect. You should see the tunnel status change to "Online" after a few moments if the connection is successful. Remember to also monitor the logs on the client and server.
|
||||
155
self-host/quick-install.mdx
Normal file
@@ -0,0 +1,155 @@
|
||||
---
|
||||
title: "Quick Install Guide"
|
||||
description: "Deploy your own Pangolin reverse proxy server in under 10 minutes with our automated installer"
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have:
|
||||
|
||||
- **Linux server** with root access and public IP address
|
||||
- **Domain name** pointing to your server's IP address for the dashboard
|
||||
- **Email address** for Let's Encrypt SSL certificates and admin log in
|
||||
- **Open ports on firewall** for 80 (TCP), 443 (TCP), and 51820 (UDP)
|
||||
|
||||
<Tip>
|
||||
**Recommended**: Ubuntu 20.04+ or Debian 11+ for best compatibility and performance.
|
||||
</Tip>
|
||||
|
||||
## Choose Your Server
|
||||
|
||||
Need help choosing? See our [complete VPS guide](/self-host/choosing-a-vps) for suggestions.
|
||||
|
||||
## DNS & Networking
|
||||
|
||||
Before installing Pangolin, ensure you've set up DNS for your domain(s) and opened the required port on your firewall. See our guide on [DNS & networking](/self-host/dns-and-networking) for more information.
|
||||
|
||||
## Installation Process
|
||||
|
||||
<Steps>
|
||||
<Step title="Download the installer">
|
||||
Connect to your server via SSH and download the installer:
|
||||
|
||||
```bash
|
||||
wget -O installer "https://github.com/fosrl/pangolin/releases/download/${release}/installer_linux_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')"
|
||||
chmod +x ./installer
|
||||
```
|
||||
|
||||
The installer supports both AMD64 (x86_64) and ARM64 architectures.
|
||||
</Step>
|
||||
|
||||
<Step title="Run the installer">
|
||||
Execute the installer with root privileges:
|
||||
|
||||
```bash
|
||||
sudo ./installer
|
||||
```
|
||||
|
||||
The installer places all files in the current directory. Move the installer to your desired installation directory before running it.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure basic settings">
|
||||
The installer will prompt you for essential configuration:
|
||||
|
||||
- **Base Domain**: Enter your root domain without subdomains (e.g., `example.com`)
|
||||
- **Dashboard Domain**: Press Enter to accept the default `pangolin.example.com` or enter a custom domain
|
||||
- **Let's Encrypt Email**: Provide an email for SSL certificates and admin login
|
||||
- **Tunneling**: Choose whether to install Gerbil for tunneled connections (default: yes). You can run Pangolin without tunneling. It will function as a standard reverse proxy.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure email (optional)">
|
||||
<Tip>
|
||||
Email functionality is optional and can be added later.
|
||||
</Tip>
|
||||
|
||||
Choose whether to enable SMTP email functionality:
|
||||
|
||||
- **Default**: No (recommended for initial setup)
|
||||
- **If enabled**: You'll need SMTP server details (host, port, username, password)
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Start installation">
|
||||
Confirm that you want to install and start the containers:
|
||||
|
||||
- The installer will pull Docker images (pangolin, gerbil, traefik)
|
||||
- Containers will be started automatically
|
||||
- This process takes 2-3 minutes depending on your internet connection
|
||||
|
||||
You'll see progress indicators as each container is pulled and started.
|
||||
</Step>
|
||||
|
||||
<Step title="Install CrowdSec (optional)">
|
||||
The installer will ask if you want to install CrowdSec for additional security:
|
||||
|
||||
- **Default**: No (recommended for initial setup)
|
||||
- **If enabled**: You'll need to confirm you're willing to manage CrowdSec configuration
|
||||
|
||||
<Warning>
|
||||
CrowdSec adds complexity and requires manual configuration for optimal security. Only enable if you're comfortable managing it.
|
||||
</Warning>
|
||||
|
||||
<Info>
|
||||
CrowdSec can be installed later if needed. The basic installation provides sufficient security for most use cases.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Post-Installation Setup
|
||||
|
||||
Once installation completes successfully, you'll see:
|
||||
|
||||
```
|
||||
Installation complete!
|
||||
|
||||
To complete the initial setup, please visit:
|
||||
https://pangolin.example.com/auth/initial-setup
|
||||
```
|
||||
|
||||
<Steps>
|
||||
<Step title="Access the dashboard">
|
||||
Navigate to the URL shown in the installer output:
|
||||
|
||||
```
|
||||
https://<your-dashboard-domain>/auth/initial-setup
|
||||
```
|
||||
|
||||
<Check>
|
||||
The dashboard should load with SSL certificate automatically configured. It might take a few minutes for the first cert to validate, so don't worry if the brwoser throws an insecure warning.
|
||||
</Check>
|
||||
</Step>
|
||||
|
||||
<Step title="Create admin account">
|
||||
Complete the initial admin user setup:
|
||||
|
||||
- Enter your admin email address
|
||||
- Set a strong password
|
||||
- Verify your email (if email is configured)
|
||||
|
||||
<Warning>
|
||||
Use a strong, unique password for your admin account. This account has full system access.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Create your first organization">
|
||||
After logging in:
|
||||
|
||||
1. Click "Create Organization"
|
||||
2. Enter organization name and description
|
||||
|
||||
<Check>
|
||||
You're now ready to start adding applications and configuring your reverse proxy!
|
||||
</Check>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Video Walkthrough
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl"
|
||||
src="https://www.youtube.com/embed/W0uVLjTyAn8"
|
||||
title="Pangolin Quick Install Guide"
|
||||
frameBorder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
128
self-host/supporter-program.mdx
Normal file
@@ -0,0 +1,128 @@
|
||||
---
|
||||
title: "Supporter Program"
|
||||
description: "Support Pangolin development and remove UI elements with a supporter key"
|
||||
---
|
||||
|
||||
Pangolin self-hosted will always be free and open source, but maintaining the project takes time and resources. The supporter program helps fund ongoing development — including bug fixes, new features, and community support.
|
||||
|
||||
## Supporter Tiers
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Limited Supporter ($25)">
|
||||
**Perfect for small teams**
|
||||
|
||||
- **User limit**: 5 or fewer users
|
||||
- **Support button**: Removed from UI
|
||||
- **Usage**: Unlimited servers and installations
|
||||
- **Upgrade**: Available to Full Supporter
|
||||
|
||||
<Warning>
|
||||
Once you add your 6th user, the support button will return. Remove a user or upgrade to Full Supporter to hide it again.
|
||||
</Warning>
|
||||
</Tab>
|
||||
|
||||
<Tab title="Full Supporter ($95)">
|
||||
**Perfect for larger teams**
|
||||
|
||||
- **User limit**: Unlimited users
|
||||
- **Support button**: Permanently removed
|
||||
- **Usage**: Unlimited servers and installations
|
||||
- **Best value**: For growing teams
|
||||
|
||||
<Check>
|
||||
The support button and other marks will never return, regardless of user count.
|
||||
</Check>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<img
|
||||
src="/images/supporter-tiers.png"
|
||||
alt="Supporter tier comparison showing Limited vs Full Supporter benefits"
|
||||
/>
|
||||
|
||||
## How to Get Your Supporter Key
|
||||
|
||||
<Steps>
|
||||
<Step title="Purchase a tier">
|
||||
Go to our [GitHub Sponsors page](https://github.com/sponsors/fosrl) and purchase either:
|
||||
|
||||
- **Limited Supporter**: $25 one-time
|
||||
- **Full Supporter**: $95 one-time
|
||||
</Step>
|
||||
|
||||
<Step title="Get your key">
|
||||
After purchase, visit [supporters.fossorial.io](https://supporters.fossorial.io) and:
|
||||
|
||||
1. Log in with your GitHub account
|
||||
2. Copy your supporter key
|
||||
</Step>
|
||||
|
||||
<Step title="Redeem in Pangolin">
|
||||
In your Pangolin dashboard:
|
||||
|
||||
1. Click the supporter button
|
||||
2. Enter your supporter key
|
||||
3. Click "Redeem"
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<img
|
||||
src="/images/redeem-key.png"
|
||||
alt="Pangolin supporter key redemption interface"
|
||||
/>
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="How many servers can I use my key on?">
|
||||
**Unlimited usage**
|
||||
|
||||
You can use your supporter key on as many servers and installations as you want. There are no restrictions on the number of deployments.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I upgrade my tier?">
|
||||
**Yes, but requires new purchase**
|
||||
|
||||
To upgrade from Limited to Full Supporter:
|
||||
|
||||
1. Purchase the Full Supporter ($95) tier on GitHub
|
||||
2. Your account will be automatically upgraded
|
||||
3. Restart your Pangolin server to update the status
|
||||
|
||||
<Warning>
|
||||
Due to GitHub's tier system, you must purchase the higher tier even if you already have the lower one. This results in an extra donation, which we appreciate!
|
||||
</Warning>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I hide the button without paying?">
|
||||
**Temporary hiding available**
|
||||
|
||||
You can click "Hide for 7 days" at the bottom of the supporter dialog to temporarily hide the button without purchasing a supporter key.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What if I buy the same tier again?">
|
||||
**Thanks for the extra donation!**
|
||||
|
||||
You can only obtain one supporter key per tier. Additional purchases of the same tier won't change your key, but we appreciate the extra support!
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I get a refund?">
|
||||
**No refunds available**
|
||||
|
||||
GitHub Sponsors does not allow us to refund donations. Please make sure you're comfortable supporting the project before purchasing a tier.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What happens if I exceed my user limit?">
|
||||
**Limited Supporter restrictions**
|
||||
|
||||
If you have a Limited Supporter key and add your 6th user:
|
||||
|
||||
- The support button will return to the UI
|
||||
- You can either remove a user or upgrade to Full Supporter
|
||||
- Your key remains valid for other installations
|
||||
|
||||
<Info>
|
||||
Full Supporter keys have no user limits.
|
||||
</Info>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -1,4 +0,0 @@
|
||||
One of the core principles of software development is DRY (Don't Repeat
|
||||
Yourself). This is a principle that applies to documentation as
|
||||
well. If you find yourself repeating the same content in multiple places, you
|
||||
should consider creating a custom snippet to keep your content in sync.
|
||||