Skip to main content

Toolbox

The Toolbox is Nekzus's one-click service deployment system that allows you to deploy pre-configured Docker Compose applications directly from the web dashboard. Services are defined using standard Docker Compose files with special labels that provide metadata and enable automatic routing.


Overview​

The Toolbox system provides:

  • One-click deployments - Deploy complex applications without writing configuration
  • Pre-configured services - Curated catalog of self-hosted applications
  • Auto-routing - Automatic route creation for deployed services
  • Auto-discovery - Deployed services are automatically discovered by Nekzus
  • Environment variable injection - Dynamic configuration at deployment time
  • Persistent storage - Volume management for data persistence
Compose-Based Architecture

The Toolbox uses standard Docker Compose files, making it easy to add new services or customize existing ones. No proprietary format required.


Configuration​

Enable the Toolbox in your config.yaml:

toolbox:
enabled: true
catalog_dir: "./toolbox" # Directory containing service templates
data_dir: "./data/toolbox" # Directory for persistent data volumes
host_data_dir: "" # Host path for Docker-in-Docker (optional)
auto_route: true # Automatically create routes for services
auto_start: true # Start containers after deployment
OptionDescriptionDefault
enabledEnable/disable the Toolbox featuretrue
catalog_dirPath to directory containing service templates./toolbox
data_dirPath for storing persistent volume data./data/toolbox
host_data_dirHost path when running in Docker-in-Docker(empty)
auto_routeCreate proxy routes automaticallytrue
auto_startStart containers after deploymenttrue

Catalog Structure​

The Toolbox catalog is organized as a directory of service subdirectories, each containing a docker-compose.yml file:

toolbox/
├── grafana/
│ └── docker-compose.yml
├── vaultwarden/
│ └── docker-compose.yml
├── memos/
│ └── docker-compose.yml
├── n8n/
│ └── docker-compose.yml
├── syncthing/
│ └── docker-compose.yml
├── dozzle/
│ └── docker-compose.yml
└── ...

Each subdirectory name becomes the service ID used in the API.

Available Services​

Nekzus includes a growing catalog of pre-configured services:

ServiceCategoryDescription
GrafanamonitoringMetrics visualization dashboards
DozzlemonitoringReal-time Docker log viewer
VaultwardensecurityBitwarden-compatible password manager
MemosproductivityPrivacy-first note-taking service
OutlineproductivityTeam knowledge base and wiki
n8nautomationWorkflow automation platform
SyncthingstorageP2P file synchronization
VikunjaproductivityOpen-source task management
GiteadevelopmentSelf-hosted Git service
PhotoPrismmediaAI-powered photo management
AdGuard HomenetworkNetwork-wide ad blocking

Creating a Toolbox Service​

To add a new service to the Toolbox catalog:

Step 1: Create Directory Structure​

mkdir toolbox/myservice

Step 2: Create docker-compose.yml​

Create a Docker Compose file with Toolbox labels:

services:
myservice:
image: vendor/myservice:latest
container_name: ${SERVICE_NAME:-myservice}
ports:
- "${APP_PORT:-8080}:8080"
volumes:
- myservice-data:/data
environment:
ADMIN_PASSWORD: "${ADMIN_PASSWORD:?Required}"
BASE_URL: "${BASE_URL}"
restart: unless-stopped
networks:
- nekzus-test-network
labels:
# Toolbox metadata (required)
nekzus.toolbox.name: "My Service"
nekzus.toolbox.category: "productivity"
nekzus.toolbox.description: "Brief description of the service"

# Toolbox metadata (optional)
nekzus.toolbox.icon: "https://example.com/icon.png"
nekzus.toolbox.tags: "tag1,tag2,tag3"
nekzus.toolbox.documentation: "https://docs.example.com"
nekzus.toolbox.image_url: "https://hub.docker.com/r/vendor/myservice"
nekzus.toolbox.repository_url: "https://github.com/vendor/myservice"

# Discovery labels (for auto-routing)
nekzus.enable: "true"
nekzus.app.id: "myservice"
nekzus.app.name: "My Service"
nekzus.route.path: "/apps/myservice/"
nekzus.route.strip_prefix: "true"

networks:
nekzus-test-network:
external: true

volumes:
myservice-data:
driver: local
labels:
nekzus.toolbox: "true"

Toolbox Labels Reference​

Required Labels​

LabelDescriptionExample
nekzus.toolbox.nameDisplay name for the service"Grafana"
nekzus.toolbox.categoryService category"monitoring"
nekzus.toolbox.descriptionBrief description"Metrics visualization"

Optional Labels​

LabelDescriptionExample
nekzus.toolbox.iconIcon URL (CDN or direct link)"https://cdn.../grafana.png"
nekzus.toolbox.tagsComma-separated search tags"monitoring,metrics,dashboards"
nekzus.toolbox.documentationLink to official docs"https://grafana.com/docs/"
nekzus.toolbox.image_urlDocker Hub or registry page"https://hub.docker.com/..."
nekzus.toolbox.repository_urlSource code repository"https://github.com/..."

Discovery Labels (Auto-Routing)​

LabelDescriptionExample
nekzus.enableEnable auto-discovery"true"
nekzus.app.idUnique application ID"grafana"
nekzus.app.nameDisplay name"Grafana"
nekzus.app.iconIcon URL for dashboard"https://..."
nekzus.route.pathRoute path prefix"/apps/grafana/"
nekzus.route.strip_prefixStrip prefix before proxying"true"
nekzus.route.rewrite_htmlRewrite HTML for sub-path hosting"true"

Environment Variables​

Toolbox services use environment variable substitution for dynamic configuration.

Variable Syntax​

SyntaxDescriptionExample
${VAR}Simple substitution${ADMIN_USER}
${VAR:-default}Default value if not set${APP_PORT:-8080}
${VAR:?error}Required, error if not set${SECRET:?Required}

Auto-Injected Variables​

Nekzus automatically injects these variables at deployment time:

VariableDescriptionExample Value
SERVICE_NAMEContainer name"my-grafana"
BASE_URLNekzus base URL"https://192.168.1.100:8443"
APP_PORTCustom port (if specified)"3001"

Variable Type Inference​

Environment variables are automatically classified by type for the UI:

  • Password fields: Variables containing password, secret, or token
  • Number fields: Variables containing port
  • URL fields: Variables containing url or host
  • Text fields: All other variables

Example: Environment Configuration​

environment:
# Simple variable with default
GF_SERVER_HTTP_PORT: "3000"

# Variable from user input with default
GF_PORT: "${GF_PORT:-3000}"

# Required password (no default)
GF_ADMIN_PASSWORD: "${GF_ADMIN_PASSWORD:?Admin password is required}"

# Uses auto-injected BASE_URL
GF_SERVER_ROOT_URL: "${BASE_URL}/apps/grafana/"

# Optional with empty default
GF_INSTALL_PLUGINS: "${GF_INSTALL_PLUGINS:-}"

Deployment Workflow​

1. Browse Catalog​

Use the web dashboard or API to browse available services:

Navigate to Toolbox in the sidebar to view the service catalog with filtering by category.

2. Configure Deployment​

Select a service and configure:

  • Service Name: Custom name for your deployment
  • Environment Variables: Required and optional configuration
  • Custom Port: Override the default host port (optional)
  • Custom Image: Use a different Docker image version (optional)

3. Deploy​

Click Deploy to start the deployment. Progress is shown in real-time.

4. Monitor Status​

Track deployment progress:

curl https://localhost:8443/api/v1/toolbox/deployments/{deployment_id}

Deployment statuses:

StatusDescription
pendingDeployment created, waiting to start
deployingDocker operations in progress
deployedService running successfully
failedDeployment failed (check error_message)
stoppedService stopped

Auto-Routing​

When auto_route: true is enabled, deployed services are automatically:

  1. Discovered by Docker discovery when the container starts
  2. Registered with the appropriate route based on discovery labels
  3. Proxied through Nekzus at the configured path

Route Configuration via Labels​

labels:
nekzus.enable: "true" # Enable discovery
nekzus.app.id: "grafana" # Unique app ID
nekzus.route.path: "/apps/grafana/" # Route path
nekzus.route.strip_prefix: "true" # Remove path prefix

After deployment, access the service at:

https://your-nekzus-host:8443/apps/grafana/

Managing Deployed Services​

List Deployments​

curl https://localhost:8443/api/v1/toolbox/deployments

Get Deployment Details​

curl https://localhost:8443/api/v1/toolbox/deployments/{deployment_id}

Remove Deployment​

curl -X DELETE https://localhost:8443/api/v1/toolbox/deployments/{deployment_id}
Data Loss

Using removeVolumes=true permanently deletes all data stored in the service's volumes.


API Reference​

Endpoints​

MethodEndpointDescription
GET/api/v1/toolbox/servicesList all available services
GET/api/v1/toolbox/services?category=monitoringFilter services by category
GET/api/v1/toolbox/services/{id}Get service details
POST/api/v1/toolbox/deployDeploy a service
GET/api/v1/toolbox/deploymentsList all deployments
GET/api/v1/toolbox/deployments?status=deployedFilter deployments by status
GET/api/v1/toolbox/deployments/{id}Get deployment status
DELETE/api/v1/toolbox/deployments/{id}Remove deployment

Request: Deploy Service​

{
"service_id": "grafana",
"service_name": "my-grafana",
"env_vars": {
"GF_ADMIN_PASSWORD": "secure-password"
},
"auto_start": true,
"custom_port": 3001,
"custom_image": "grafana/grafana:10.0.0"
}
FieldTypeRequiredDescription
service_idstringYesTemplate ID from catalog
service_namestringYesCustom name for deployment
env_varsobjectNoEnvironment variables
auto_startbooleanNoStart container after creation
custom_portintegerNoOverride default host port
custom_imagestringNoOverride Docker image

Response: Deployment Created​

{
"deployment_id": "deploy_1703505600000000000",
"status": "pending",
"message": "Deployment 'my-grafana' initiated"
}

Response: Deployment Status​

{
"id": "deploy_1703505600000000000",
"service_template_id": "grafana",
"service_name": "my-grafana",
"status": "deployed",
"project_name": "deploy_1703505600000000000",
"env_vars": {
"GF_ADMIN_PASSWORD": "***"
},
"deployed_at": "2024-12-25T10:00:00Z",
"created_at": "2024-12-25T09:59:55Z",
"updated_at": "2024-12-25T10:00:00Z"
}

Example Service Configurations​

Monitoring: Grafana​

services:
grafana:
image: grafana/grafana:latest
container_name: ${SERVICE_NAME:-grafana}
ports:
- "${GF_PORT:-3000}:3000"
volumes:
- grafana-data:/var/lib/grafana
environment:
GF_SERVER_ROOT_URL: "${BASE_URL}/apps/grafana/"
GF_SECURITY_ADMIN_PASSWORD: "${GF_ADMIN_PASSWORD}"
GF_INSTALL_PLUGINS: "${GF_INSTALL_PLUGINS:-}"
restart: unless-stopped
networks:
- nekzus-test-network
labels:
nekzus.toolbox.name: "Grafana"
nekzus.toolbox.icon: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/grafana.png"
nekzus.toolbox.category: "monitoring"
nekzus.toolbox.tags: "monitoring,metrics,dashboards,visualization"
nekzus.toolbox.description: "Beautiful monitoring and analytics dashboards"
nekzus.toolbox.documentation: "https://grafana.com/docs/grafana/latest/"

nekzus.enable: "true"
nekzus.app.id: "grafana"
nekzus.app.name: "Grafana"
nekzus.route.path: "/apps/grafana/"
nekzus.route.strip_prefix: "true"

networks:
nekzus-test-network:
external: true

volumes:
grafana-data:
driver: local
labels:
nekzus.toolbox: "true"

Security: Vaultwarden​

services:
vaultwarden:
image: vaultwarden/server:latest
container_name: ${SERVICE_NAME:-vaultwarden}
ports:
- "${VAULTWARDEN_PORT:-8080}:80"
volumes:
- vaultwarden-data:/data
environment:
DOMAIN: "${BASE_URL}/apps/vaultwarden"
SIGNUPS_ALLOWED: "${SIGNUPS_ALLOWED:-true}"
ADMIN_TOKEN: "${ADMIN_TOKEN:?Required - generate with openssl rand -base64 48}"
WEBSOCKET_ENABLED: "true"
restart: unless-stopped
networks:
- nekzus-test-network
labels:
nekzus.toolbox.name: "Vaultwarden"
nekzus.toolbox.icon: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/vaultwarden.png"
nekzus.toolbox.category: "security"
nekzus.toolbox.tags: "passwords,security,bitwarden,vault,secrets"
nekzus.toolbox.description: "Lightweight Bitwarden-compatible password manager"
nekzus.toolbox.documentation: "https://github.com/dani-garcia/vaultwarden/wiki"

nekzus.enable: "true"
nekzus.app.id: "vaultwarden"
nekzus.app.name: "Vaultwarden"
nekzus.route.path: "/apps/vaultwarden/"
nekzus.route.strip_prefix: "true"

networks:
nekzus-test-network:
external: true

volumes:
vaultwarden-data:
driver: local
labels:
nekzus.toolbox: "true"

Automation: n8n​

services:
n8n:
image: n8nio/n8n:latest
container_name: ${SERVICE_NAME:-n8n}
ports:
- "${N8N_PORT:-5678}:5678"
volumes:
- n8n-data:/home/node/.n8n
environment:
N8N_HOST: "0.0.0.0"
N8N_PORT: "5678"
WEBHOOK_URL: "${BASE_URL}/apps/n8n/"
N8N_EDITOR_BASE_URL: "${BASE_URL}/apps/n8n/"
N8N_BASIC_AUTH_ACTIVE: "true"
N8N_BASIC_AUTH_USER: "${N8N_USER:-admin}"
N8N_BASIC_AUTH_PASSWORD: "${N8N_PASSWORD:?Required}"
GENERIC_TIMEZONE: "${TIMEZONE:-UTC}"
restart: unless-stopped
networks:
- nekzus-test-network
labels:
nekzus.toolbox.name: "n8n"
nekzus.toolbox.icon: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/n8n.png"
nekzus.toolbox.category: "automation"
nekzus.toolbox.tags: "automation,workflow,integration,zapier-alternative"
nekzus.toolbox.description: "Workflow automation tool"
nekzus.toolbox.documentation: "https://docs.n8n.io/"

nekzus.enable: "true"
nekzus.app.id: "n8n"
nekzus.app.name: "n8n"
nekzus.route.path: "/apps/n8n/"
nekzus.route.strip_prefix: "true"

networks:
nekzus-test-network:
external: true

volumes:
n8n-data:
driver: local
labels:
nekzus.toolbox: "true"

Service Categories​

Organize services into categories for easier browsing:

CategoryDescriptionExamples
monitoringMetrics, logs, observabilityGrafana, Dozzle, Prometheus
productivityNotes, tasks, knowledge basesMemos, Outline, Vikunja
securityPassword managers, secretsVaultwarden
automationWorkflows, integrationsn8n, Huginn
storageFile sync, backupSyncthing, Nextcloud
mediaPhotos, videos, musicPhotoPrism, Jellyfin
developmentGit, CI/CD, IDEsGitea, Drone
networkDNS, VPN, ad-blockingAdGuard Home, Pi-hole

Best Practices​

Security​

Secure Secrets

Always use ${VAR:?error} syntax for sensitive values to ensure they are provided at deployment time. Never commit secrets to version control.

  • Use strong passwords with the :? required syntax
  • Enable HTTPS for services handling sensitive data
  • Regularly update container images
  • Review security notes in service templates

Networking​

  • Use external networks for inter-service communication
  • Configure BASE_URL correctly for reverse proxy setups
  • Test routes after deployment

Data Persistence​

  • Use named volumes for persistent data
  • Back up volume data regularly
  • Label volumes with nekzus.toolbox: "true" for identification

Resource Management​

  • Monitor container resource usage
  • Set memory limits for resource-intensive services
  • Use appropriate restart policies

Troubleshooting​

Deployment Fails with "Port Conflict"​

The requested port is already in use. Solutions:

  1. Use custom_port to specify a different port
  2. Stop the conflicting service
  3. Check with docker ps for running containers

Service Not Accessible After Deployment​

  1. Verify the container is running: docker ps
  2. Check container logs: docker logs <container_name>
  3. Confirm discovery labels are correct
  4. Verify the route was created in Nekzus

Environment Variable Not Substituted​

  1. Ensure variable name matches exactly (case-sensitive)
  2. Check for typos in ${VAR} syntax
  3. Verify the variable is passed in env_vars during deployment

Container Keeps Restarting​

  1. Check container logs for errors
  2. Verify required environment variables are set
  3. Ensure volume paths exist and are writable
  4. Check for port conflicts