Thanks for your interest in contributing! Docker Dash is actively maintained and welcomes contributions of all sizes — from typo fixes to new features.
No build step required. Edit any .js or .css file, refresh the browser, and see your changes immediately. This is the simplest Docker management dashboard to contribute to.
Looking for where to start? These are great first contributions:
- Add a language translation — copy
public/js/i18n/TEMPLATE.js, translate values, add one<script>tag. Currently: 11 languages (EN complete, RO ~77%, others ~66% — translations tab in System covers the gap without code). - Add an app template — add an entry to
src/routes/templates.js(JSON object with compose YAML). Currently: 47 templates including the AI Workload Pack (12 entries: Ollama, RAG stack, vLLM, etc.). - Improve i18n coverage — some pages still have hardcoded English strings (grep for strings not using
i18n.t()) - Add tests — 1122 tests across 70 suites; more coverage is always welcome, especially integration tests for newer features (registry retention, pCloud orchestration, AI provider abstraction)
- Documentation — improve README, add examples, write tutorials
- Accessibility — add ARIA attributes, improve screen reader support, test keyboard navigation
- 30+ pages in the frontend SPA (incl. Registry Browse, AI Settings, Observability Wizard, pCloud Backup card)
- 451 API endpoints (
grep -hE "router\.(get|post|put|patch|delete)" src/routes/*.js | wc -l) - 1122 tests (70 test suites, 100% passing — 4 skipped are live-Cloudflare ACME integration gated on a CI secret)
- 47 app templates including the v8.0.1 AI Workload Pack (Ollama / Open WebUI / RAG / vLLM / SD / ComfyUI / Whisper / Langflow / AnythingLLM / n8n / LiteLLM / Flowise)
- 64 database migrations (001-064)
- 11 languages — EN (complete), RO (~77%), DE/IT/FR/ES/PT/ZH/JA/KO/tlh (~66%, fall back to EN for missing keys)
- 84 built-in How-To guides (EN + RO content; AI category covers Ollama on CPU/GPU + GPU passthrough + RAG stack)
- Fork the repository on GitHub
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/docker-dash.git cd docker-dash - Create a branch for your work:
git checkout -b feature/my-feature
- Install dependencies:
npm install
- Configure environment:
cp .env.example .env
- Start dev server (auto-reloads on changes):
npm run dev
- Open http://localhost:8101 — login with
admin/admin
plans/is gitignored — strategic specs live there locally. Major features (anything that ships in a.0minor) follow the deep-spec → feature-spec → code → tests pipeline. Specs are local-only by design (one author, multiple iterations, no need to pollute repo history). Contributors can request a spec walkthrough via Discussion if they want to understand the "why" behind a major feature.CLAUDE.mdat repo root documents the project conventions for AI-assisted contributions. Read it if you're using Claude Code or any AI agent to contribute.- Anti-features are documented WITH RATIONALE. When a feature is not shipped, the deep-spec or CHANGELOG section says why. Examples: "no always-on AI chat sidebar" (v8.0.0), "no pCloud OAuth" (v8.2.0), "no automatic registry GC" (v7.6.0).
These are non-negotiable design decisions. Please respect them in your contributions:
- No build step — The frontend is vanilla JavaScript loaded directly by the browser. No webpack, Vite, Rollup, or any bundler. Files in
public/are served as-is. - No frontend framework — No React, Vue, Svelte, or Angular. All UI is built with plain DOM manipulation. This keeps the project dependency-free on the frontend.
- CDN for frontend libraries — xterm.js, Chart.js, Font Awesome are loaded from CDN (jsDelivr). Don't add npm packages for frontend use.
- SQLite embedded — No external database. No PostgreSQL, no Redis. SQLite is the database and it runs in-process. Migrations auto-apply on startup.
- CommonJS — Backend uses
require()/module.exports. No ESM imports. - Single CSS file — All styles live in
public/css/app.cssusing CSS custom properties (variables) for theming.
'use strict'at the top of every file- Single quotes for strings
- No semicolon-free style — always use semicolons
- Functions should be short and focused (under 50 lines ideally)
- No TypeScript — this is a vanilla JS project by design
- No classes on the frontend — use plain objects with methods
-
Create
public/js/pages/mypage.js:'use strict'; const MyPage = { async render(container, params) { container.innerHTML = ` <div class="page-header"> <h2><i class="fas fa-icon"></i> ${i18n.t('pages.mypage.title')}</h2> </div> <div id="mypage-content"></div> `; // Load data, bindevents, etc. }, destroy() { // Cleanup: remove event listeners, stop intervals, etc. }, }; window.MyPage = MyPage;
-
Register in
public/js/app.js→_pagesobject:_pages: { // ...existing pages... mypage: () => MyPage, },
-
Add nav item in
public/index.html:<a href="#/mypage" class="nav-item" data-page="mypage"> <i class="fas fa-icon"></i><span>My Page</span> </a>
-
Add to command palette in
app.js→_getCommands():{ icon: 'fa-icon', label: i18n.t('nav.mypage'), action: () => this.navigate('/mypage'), section: 'nav' },
-
Add translations in
public/js/i18n.js— both EN and RO sections:// EN nav: { /* ... */ mypage: 'My Page' }, pages: { mypage: { title: 'My Page' } }, // RO nav: { /* ... */ mypage: 'Pagina Mea' }, pages: { mypage: { title: 'Pagina Mea' } },
-
Add
<script>tag inpublic/index.html(beforeapp.js):<script src="/js/pages/mypage.js?v=5.4"></script>
-
Create or edit a route file in
src/routes/:router.get('/my-endpoint', requireAuth, async (req, res) => { try { const data = await someService.getData(req.hostId); res.json(data); } catch (err) { res.status(500).json({ error: err.message }); } });
-
Mount in
src/server.jsif it's a new file:app.use('/api/myroute', apiLimiter, require('./routes/myroute'));
-
Add the
extractHostIdmiddleware if the endpoint interacts with Docker:const { extractHostId } = require('../middleware/hostId'); router.use(extractHostId);
-
Add client method in
public/js/api.js:getMyData() { return this.get('/myroute/my-endpoint'); },
Note:
hostIdis automatically appended to API calls.
Migrations live in src/db/migrations/ and run automatically on startup in order.
Create a new file with the next sequential number:
// src/db/migrations/013_my_table.js
'use strict';
exports.up = function(db) {
db.exec(`
CREATE TABLE IF NOT EXISTS my_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
)
`);
};Rules:
- Always use
CREATE TABLE IF NOT EXISTSortry/catchforALTER TABLE - Include
host_idcolumn if the data is per-Docker-host - Add indexes for columns used in WHERE/ORDER BY
- Never modify existing migration files — create a new one instead
All colors use CSS variables defined in public/css/app.css:
/* Use these instead of hardcoded colors */
var(--text) /* Primary text */
var(--text-dim) /* Secondary/muted text */
var(--accent) /* Primary accent (blue) */
var(--green) /* Success/running */
var(--red) /* Error/danger */
var(--yellow) /* Warning */
var(--surface) /* Card backgrounds */
var(--surface2) /* Code blocks, subtle backgrounds */
var(--border) /* Borders */Always test your changes in both dark and light themes.
Before submitting a PR, verify:
-
node --checkpasses on all modified.jsfiles -
npm testpasses (currently 1122 tests at 100%) -
npm run lintpasses (0 errors) - Works in both dark and light themes
- Works with sidebar collapsed and expanded
- Works on mobile (768px and 480px breakpoints)
- Translations added for EN (RO and DE appreciated but not required)
- No console.log in production code (use
log.info/warn/error) - No
execSyncwith template literals — useexecFileSyncwith argument arrays - No hardcoded colors — use CSS variables
- API endpoints include
extractHostIdif they touch Docker resources - Sensitive data (tokens, keys) encrypted with
crypto.encrypt(), never stored in plaintext - PR is focused on a single feature or fix
- Commit messages follow conventional format (
feat:,fix:,chore:,test:)
# Run all tests
npm test
# Run specific test file
npx jest auth-flow --verbose
# Run with watch mode
npm run test:watch
# Run linter
npm run lintUse GitHub Issues with our templates:
- Bug Report — include browser, OS, Docker version, steps to reproduce
- Feature Request — describe the problem, proposed solution, use case
For security vulnerabilities, see SECURITY.md — do not open public issues.
Use GitHub Discussions:
- Q&A — for support questions
- Ideas — for feature suggestions
- Show and tell — share your Docker Dash setup
By contributing, you agree that your contributions will be licensed under the MIT License.