Custom configurations can be seen as overlaying the DevBase core configuration with customizations. For example:
-
You require a proxy for internet access
-
You need custom CA certificates
-
You need to configure container/package registries
-
You want to share SSH configurations
This guide shows how to create a devbase-custom-config/ directory to configure DevBase for your organization.
Organizations can customize via a devbase-custom-config/ directory overlay:
devbase-custom-config/
├── config/
│ └── org.env # Organization variables
├── certificates/ # CA certificates (*.crt)
├── packages/ # Package overrides (optional)
│ └── packages-custom.yaml # Override/extend default packages
├── templates/ # Config templates
├── ssh/ # SSH configuration
│ └── custom.config # Organization SSH hosts
├── git-hooks/ # Custom git hooks
│ ├── pre-commit.d/
│ └── commit-msg.d/
├── hooks/ # Installation lifecycle hooks
│ ├── pre-install.sh # Before installation
│ ├── post-configuration.sh # After config, before tools
│ └── post-install.sh # After everything
└── verification/ # Custom verification
└── verify-custom.sh# Method 1: Auto-detect sibling directory
cd devbase-core
./setup.sh # Finds ../devbase-custom-config/
# Method 2: Explicit path
export DEVBASE_CUSTOM_DIR=/path/to/custom
./setup.sh# Email domain (pre-fills during installation)
DEVBASE_EMAIL_DOMAIN="@company.com"
# Network - Proxy configuration
DEVBASE_PROXY_HOST="proxy.company.com"
DEVBASE_PROXY_PORT="8080"
DEVBASE_NO_PROXY_DOMAINS="*.company.com,localhost"
# Container registry (optional)
DEVBASE_REGISTRY_HOST="registry.company.com"
DEVBASE_REGISTRY_PORT="5000"|
Note
|
Configure /etc/hosts manually if you need custom host entries. Use hooks for automation.
|
When DEVBASE_PROXY_HOST and DEVBASE_PROXY_PORT are set, DevBase automatically:
-
Configures environment variables:
-
Sets standard proxy variables:
HTTP_PROXY,HTTPS_PROXY,NO_PROXY(and lowercase variants) -
Creates Fish shell configuration:
~/.config/fish/conf.d/00-proxy.fish
-
-
Configures development tools:
-
Java: Sets
JAVA_TOOL_OPTIONSwith proxy settings (host, port, nonProxyHosts) -
Gradle: Sets
GRADLE_OPTSwith proxy settings -
Git: Configures
http.proxyandhttps.proxyglobally -
Snap: Configures system-wide Snap proxy settings
-
Docker (optional): Creates a systemd drop-in for Docker daemon proxy when proxy variables are set
-
-
Configures system:
-
Sudo: Preserves proxy environment variables when using sudo
-
Creates
/etc/sudoers.d/devbase-keep-proxy-env
-
-
Installs proxy management function:
-
Installs
devbase-proxy.fishfunction to~/.config/fish/functions/ -
Provides interactive proxy control:
devbase-proxy on|off|status -
Manages APT and Snap proxy settings dynamically
-
Proxy management commands:
devbase-proxy status # Show current proxy status
devbase-proxy on # Enable proxy for current session
devbase-proxy off # Disable proxy for current sessionPlace organization certificates in devbase-custom-config/certificates/:
devbase-custom-config/
└── certificates/
├── root-ca.crt # Your organization's root CA
├── intermediate-ca.crt # Intermediate CA if needed
├── gitlab.example.com.crt # Git server certificate
└── registry.example.com.crt # Container registry certificateWhat happens:
-
All
.crtfiles are automatically copied to/usr/local/share/ca-certificates/ -
Registered with the system certificate store via
update-ca-certificates -
Available to all applications (Git, curl, browsers, etc.)
Certificate requirements:
-
Must be in PEM format with
.crtextension -
Files must be in the root of
certificates/directory (subdirectories not processed)
Certificate format example:
-----BEGIN CERTIFICATE-----
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
...
-----END CERTIFICATE-----Validation:
DevBase validates certificates before installation:
openssl x509 -in certificate.crt -noout # Rejects invalid certificatesDevBase allows customization of SSH key generation through org.env.
Available variables:
# SSH key type: ed25519 (default), ecdsa, ed25519-sk, ecdsa-sk
DEVBASE_SSH_KEY_TYPE="ed25519"
# SSH key filename (created in ~/.ssh/)
DEVBASE_SSH_KEY_NAME="id_ed25519_devbase"Supported key types:
| Type | Description |
|---|---|
|
Modern EdDSA algorithm - fast, secure, small keys (recommended for most users) |
|
Elliptic Curve DSA using P-521 curve (highest security ECDSA option) |
|
EdDSA with FIDO/U2F hardware security key support (requires physical device) |
|
ECDSA with FIDO/U2F hardware security key support (requires physical device) |
|
Note
|
ECDSA in DevBase uses the P-521 curve (521-bit) for maximum security. |
Configuration examples:
Ed25519 (default):
# In org.env
DEVBASE_SSH_KEY_TYPE="ed25519"
DEVBASE_SSH_KEY_NAME="id_ed25519_mycompany"ECDSA P-521:
# In org.env
DEVBASE_SSH_KEY_TYPE="ecdsa"
DEVBASE_SSH_KEY_NAME="id_ecdsa_521_mycompany"Hardware security key:
# In org.env (requires FIDO/U2F device like YubiKey)
DEVBASE_SSH_KEY_TYPE="ed25519-sk"
DEVBASE_SSH_KEY_NAME="id_ed25519_sk_yubikey"Best practices:
-
Use Ed25519 for general use (default, fastest, most secure)
-
Use ECDSA for NIST compliance requirements
-
Include key type in filename for clarity
-
Hardware security keys provide maximum security but require physical device
Provide organization SSH hosts in devbase-custom-config/ssh/custom.config:
# Organization SSH Configuration
Host github.com
ProxyCommand nc -X connect -x proxy.company.com:8080 %h %p
Host gitlab.internal.company.com
HostName gitlab.internal.company.com
IdentityFile ~/.ssh/id_ed25519_devbase
ProxyCommand none
Host *.internal.company.com
ProxyCommand noneBest practices:
-
Only include hosts users actually need
-
Use wildcards for patterns (e.g.,
*.company.com) -
Keep minimal - users need flexibility
-
Users can override these settings in their personal
user.config
Place files in devbase-custom-config/templates/. DevBase processes different file types automatically.
DevBase ships with default templates.
Override any template by creating a file with the same name in devbase-custom-config/templates/:
Available DevBase templates:
Shell & Terminal:
-
00-environment.fish.template(Fish) -
01-keybindings.fish.template(Fish) -
02-aliases.fish.template(Fish) -
starship.toml.template(Starship) -
config.kdl.template(Zellij)
Development Tools:
-
config.template(Git) -
config.yml.template(LazyGit) -
colorscheme.lua.template(Neovim) -
btop.conf.template(btop)
To see all available templates: find dot/ -name "*.template" -type f
How overrides work:
-
DevBase validates: custom template filename matches a vanilla template
-
If match found: custom template replaces vanilla
-
If no match: warning shown, template skipped
Create organization-specific templates to override DevBase defaults or add new configurations.
Built-in Templates (automatically configured from devbase-core):
Maven (YAML-based, conditionally processed based on registry/proxy configuration):
- Generated from YAML fragments → ~/.m2/settings.xml
- Base fragments in devbase_files/maven-templates/yaml/
- Custom repositories via maven-repos.yaml in templates/ (optional)
Maven YAML Template System:
Instead of maintaining multiple monolithic XML templates, Maven settings are composed from YAML fragments:
Files:
- base.yaml - XML namespaces and document structure
- proxy.yaml - HTTP/HTTPS proxy configuration (included if DEVBASE_PROXY_HOST and DEVBASE_PROXY_PORT set)
- registry.yaml - Internal registry mirrors (included if DEVBASE_REGISTRY_HOST and DEVBASE_REGISTRY_PORT set)
How it works:
1. Fragments selected based on environment variables
2. Environment variables substituted using envsubst
3. YAML fragments merged using yq eval-all
4. Merged YAML converted to XML with yq -o=xml
Benefits: - ✓ No duplication - Each setting defined once - ✓ Clean separation - Organization repos in custom config - ✓ All combinations - Handles proxy-only, registry-only, or both - ✓ Readable - YAML easier to edit than XML
Custom Maven Repositories:
In devbase-custom-config/templates/maven-repos.yaml:
profiles:
profile:
- id: org-repos
repositories:
repository:
- id: org-snapshots
name: Organization Snapshots
url: ${DEVBASE_REGISTRY_URL}/repository/maven-snapshots/
snapshots:
enabled: "true"
- id: org-releases
name: Organization Releases
url: ${DEVBASE_REGISTRY_URL}/repository/maven-releases/
releases:
enabled: "true"
activeProfiles:
activeProfile:
- org-reposRequirements: yq (mikefarah/yq) version 4.x or later with XML output support (installed via mise)
Fallback: If custom XML templates exist in devbase-custom-config/templates/, the legacy XML-based approach is used instead
Gradle (only if registry configured):
- init.gradle.template → ~/.gradle/init.gradle
Container Registry (only if registry configured):
- registries.conf.template → ~/.config/containers/registries.conf
Custom Configuration Templates (.template suffix - variable substitution with envsubst):
You can override built-in templates or add your own:
-
maven-repos.yaml→ Merged into~/.m2/settings.xml(organization-specific Maven repositories) -
init.gradle.template→~/.gradle/init.gradle(override built-in) -
registries.conf.template→~/.config/containers/registries.conf(override built-in) -
.testcontainers.properties→~/.testcontainers.properties(Testcontainers configuration, overrides built-in) -
npmrc.template→~/.npmrc(NPM registry configuration) -
gradle.properties.template→~/.gradle/gradle.properties(Gradle properties)
Generic:
-
{name}.template→~/.{name}(Any other dotfile - uses filename as-is)
Append Files (.append suffix - content appended to existing files):
-
known_hosts.append→~/.ssh/known_hosts(SSH known hosts) -
bashrc.append→~/.bashrc(Bash configuration)
Other File Types:
-
*.service→~/.config/systemd/user/(systemd user services) -
.confor.config→~/.config/{filename}(config files, copied as-is) -
Files with extensions →
~/.{filename}(dotfiles, copied as-is) -
Files without extensions →
~/.local/bin/{filename}(executable scripts)
Templates support environment variable substitution:
# Available variables (constructed from HOST+PORT during setup)
${DEVBASE_REGISTRY_URL} # Container registry URL (built from HOST+PORT for Maven/Gradle)
${DEVBASE_EMAIL_DOMAIN} # Email domain
${USER} # Username
${HOME} # Home directory
${DEVBASE_PROXY_HOST} # Proxy hostname
${DEVBASE_PROXY_PORT} # Proxy port
# Syntax
${VARIABLE_NAME} # Replaced with value
${VARIABLE_NAME:-default} # Use default if not setVariables from org.env are automatically available.
DevBase uses a unified packages.yaml configuration that defines all packages (APT, Snap, mise tools, VS Code extensions, and custom installers) in a single file. Organizations can customize packages using a packages-custom.yaml overlay.
devbase-custom-config/
└── packages/
└── packages-custom.yaml # Override/extend default packagesDevBase uses deep merge via yq to combine base and custom configurations:
-
Custom file exists → Merge with DevBase defaults (custom values override)
-
Custom file missing → Use DevBase defaults only
This allows organizations to:
-
Override specific package versions
-
Add new packages to existing packs
-
Define entirely new language packs
-
Remove packages by setting empty values
The unified configuration has two main sections:
# core: Always installed (essential tools)
core:
apt:
curl: {}
git: {}
snap:
chromium: {}
mise:
fzf: { backend: "aqua:junegunn/fzf", version: "v0.67.0" }
vscode:
redhat.vscode-yaml: { version: "1.17.0" }
custom:
mise: { version: "v2025.9.20", installer: "install_mise" }
# packs: User-selectable language bundles
packs:
java:
description: "Java/JVM development"
apt:
default-jdk: {}
mise:
java: { version: "temurin-21" }
maven: { version: "3.9.6" }
vscode:
redhat.java: { version: "1.40.0" }
node:
description: "Node.js development"
mise:
node: { version: "24.11.1" }Create devbase-custom-config/packages/packages-custom.yaml:
# Organization Package Customizations
# This file is merged with DevBase defaults
# Override core packages
core:
apt:
# Add organization-specific packages
your-company-tool: {}
mise:
# Override a tool version
fzf: { backend: "aqua:junegunn/fzf", version: "v0.66.0" }
# Extend or override language packs
packs:
java:
mise:
# Add Kotlin to Java pack
kotlin: { version: "2.0.0" }
# Override Maven version
maven: { version: "3.9.9" }
custom:
# Add organization Java tools
internal-java-sdk: { version: "1.0.0", installer: "install_internal_sdk" }
# Define entirely new pack
company-tools:
description: "Internal company tools"
apt:
company-cli: {}
mise:
internal-tool: { backend: "github:company/tool", version: "v1.0.0" }apt:
package-name: {} # Simple package
package-with-tag: { tags: ["@skip-wsl"] } # Skip on WSLsnap:
chromium: {} # Default options
ghostty: { options: "--classic" } # Classic confinement
some-app: { options: "--channel=edge" } # Specific channel
desktop-app: { options: "--classic", tags: ["@skip-wsl"] }mise:
# Simple (no backend)
java: { version: "temurin-21" }
# With backend
fzf: { backend: "aqua:junegunn/fzf", version: "v0.67.0" }
# GitLab backend
glab: { backend: "gitlab:gitlab-org/cli", version: "v1.68.0" }vscode:
extension.id: { version: "1.0.0" } # Pinned version
optional.extension: { version: "2.0.0", tags: ["@optional"] } # User promptedDuring installation, users select which language packs to install:
Which language packs do you want to install?
[x] java - Java/JVM development (Spring Boot, Maven, Gradle)
[x] node - Node.js/JavaScript/TypeScript development
[x] python - Python development (Django, Flask, data science)
[x] go - Go/Golang development
[x] ruby - Ruby/Rails development
[ ] rust - Rust development (optional)
[x] vscode-editor - VS Code editor with full configuration
Space=toggle, Enter=confirmAll packs except rust are selected by default. Selection is saved to preferences.yaml for future updates.
# If custom packages exist
Merging custom package configuration...
Found packages-custom.yaml overlay
# Pack selection
Selected packs: java, node, pythonDevBase supports global git hooks using a dispatcher pattern. When enabled during installation, hooks are configured to run automatically on git operations.
|
Note
|
Git hooks are opt-in during installation (default: No). Existing hooks are backed up to ${XDG_DATA_HOME}/devbase/backup/git-hooks/ before installation.
|
Git hooks use a dispatcher pattern - each hook runs all scripts in its corresponding .d/ directory:
~/.config/git/git-hooks/
├── pre-commit # Dispatcher
├── pre-commit.d/
│ └── 01-secrets-scan.sh # Active: Gitleaks secret scanning
├── commit-msg # Dispatcher
├── post-commit.d/
│ └── 01-conventional-commits.sh # Active: Conventional commits (if .conform.yaml exists)
├── prepare-commit-msg # Dispatcher
├── prepare-commit-msg.d/
│ └── 01-add-issue-ref.sh # Active: Auto-add Refs: from branch
├── pre-push # Dispatcher
└── pre-push.d/ # (empty by default)DevBase Core provides minimal hooks focused on security and workflow automation. Project-specific linting (shellcheck, hadolint, etc.) should be configured per-project using devbase-check or similar tooling.
-
01-secrets-scan.sh - Scans staged files for secrets using gitleaks before commit. Prevents accidentally committing API keys, passwords, and tokens.
-
01-conventional-commits.sh - Validates commit policy using conform. Only active if the repository has a
.conform.yamlconfig file. -
01-add-issue-ref.sh - Automatically adds
Refs: ISSUE-123footer to commit messages when the branch name contains an issue reference (e.g.,feature/PROJ-456-add-feature).
When you run git commit:
-
pre-commit dispatcher runs secret scanning on staged files
-
If secrets detected → commit is blocked
-
prepare-commit-msg dispatcher runs, auto-adding issue references from branch name
-
User edits commit message
-
Git creates the commit
-
post-commit dispatcher validates commit policy (non-blocking, if
.conform.yamlexists)
When you run git push:
-
pre-push dispatcher runs any custom checks you have configured
Personal hooks:
cd ~/.config/git/git-hooks/pre-commit.d/
nano 10-my-custom-check.sh
chmod +x 10-my-custom-check.shOrganization hooks:
Place scripts in devbase-custom-config/git-hooks/:
devbase-custom-config/
└── git-hooks/
├── pre-commit.d/
│ └── 99-company-policy.sh
└── commit-msg.d/
└── 99-jira-integration.shThese are automatically overlaid during installation.
|
Warning
|
Existing hooks from templates (01-, 02-, 03-) are overwritten on re-install to ensure updates. User-added hooks (50-, 99-*) are preserved. |
Scripts run in sorted alphabetical order. Use numeric prefixes:
-
01-49- DevBase core checks (updated on re-install) -
50-99- User/team custom checks (preserved on re-install)
Globally:
git config --global --unset core.hooksPathPer repository:
cd your-repo
git config --unset core.hooksPathBypass once:
git commit --no-verify # Skips pre-commit and commit-msg
git push --no-verify # Skips pre-push|
Note
|
During non-interactive rebase (git rebase main), pre-commit and commit-msg hooks do not re-run on existing commits.
|
Interactive rebase (git rebase -i):
- Hooks run when you edit or reword commits
- Hooks do not run for commits you pick
Enforcement: Use repository policies (for example, conform in CI) to ensure rebased commits still comply.
Custom hooks allow a user to run scripts at specific points during installation. Hooks must:
-
Be executable (
chmod +x) -
Have a proper shebang (
#!/usr/bin/env bash) -
Be valid bash syntax
Place hooks in devbase-custom-config/hooks/:
Runs before main installation starts.
Use for: - Validating prerequisites - Setting environment variables - Checking organization requirements
Example:
#!/usr/bin/env bash
# Check for required network access
if ! curl -s https://internal.company.com > /dev/null; then
echo "ERROR: Cannot reach internal network"
exit 1
fiRuns after system configuration, before finalization.
Use for: - Modifying installed configurations - Setting up organization-specific services - Configuring tools that were just installed
A user can add custom verification checks to ensure organization-specific requirements are met.
Create devbase-custom-config/verification/verify-custom.sh:
#!/usr/bin/env bash
set -uo pipefail
# Organization Custom Verification Script
#
# IMPORTANT: This script is sourced by the main verification script.
# Follow these conventions:
# - Use 'local' for all internal variables
# - Use 'return' not 'exit' in functions
# - Only modify: PASSED_CHECKS, FAILED_CHECKS, WARNING_CHECKS, TOTAL_CHECKS
# Load base verification library
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ -n "${DEVBASE_ROOT:-}" ]]; then
VERIFY_LIB="${DEVBASE_ROOT}/verify/verify-base-lib.sh"
else
VERIFY_LIB="${SCRIPT_DIR}/../../devbase-core/verify/verify-base-lib.sh"
fi
if [[ -f "$VERIFY_LIB" ]]; then
source "$VERIFY_LIB"
else
echo "Error: verify-base-lib.sh not found at $VERIFY_LIB"
return 1
fi
# Your custom checks here
check_proxy_configuration() {
print_header "Organization Proxy Configuration"
local npm_proxy=$(npm config get proxy 2>/dev/null)
if [[ "$npm_proxy" == *"company.com"* ]]; then
print_check "pass" "NPM proxy: $npm_proxy"
else
print_check "fail" "NPM proxy not configured for organization"
fi
}
check_internal_certificates() {
print_header "Organization Certificates"
local certs=("CompanyRootCA.crt" "CompanyInternalCA.crt")
for cert in "${certs[@]}"; do
if [[ -f "/usr/local/share/ca-certificates/$cert" ]]; then
print_check "pass" "$cert installed"
else
print_check "fail" "$cert missing"
fi
done
}
main() {
check_proxy_configuration
check_internal_certificates
if [[ ${FAILED_CHECKS:-0} -gt 0 ]]; then
return 1
fi
return 0
}
# Handle both sourced and direct execution
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
exit $?
else
main "$@"
fiMake it executable:
chmod +x devbase-custom-config/verification/verify-custom.shImportant conventions:
-
The script is sourced by the main verification, not run as a subprocess
-
Use
returninstead ofexitin all functions (exit would kill the parent process) -
Always declare variables with
localto avoid namespace pollution -
The script automatically contributes to the overall verification summary counters
After installation, run:
./verify/verify-install-check.shThe main verification script will automatically discover and run your custom verification if it exists at devbase-custom-config/verification/verify-custom.sh.
The verify-base-lib.sh library provides these functions, which a customization could use:
Display Functions:
-
print_header "Section Title"- Print section header -
print_subheader "Subsection"- Print subsection header -
print_check "pass|fail|warn|info" "message"- Print check result -
display_file_box "file" [width]- Display file contents in a box
File Operations:
-
file_exists "path"- Check if file exists -
dir_exists "path"- Check if directory exists -
check_file_content "file" "pattern" "success_msg" "fail_msg"- Check file contains pattern
Environment:
-
check_env_var "VAR_NAME" [max_length] [mask_password]- Check and display environment variable -
has_command "cmd"- Check if command exists
Proxy Checks:
-
check_npm_proxy- Verify NPM proxy configuration -
check_git_proxy- Verify Git proxy configuration -
check_maven_proxy- Verify Maven proxy configuration -
check_gradle_proxy- Verify Gradle proxy configuration
Counters:
The library maintains global counters that your custom checks update automatically:
-
TOTAL_CHECKS- Total number of checks run -
PASSED_CHECKS- Number of passed checks -
FAILED_CHECKS- Number of failed checks -
WARNING_CHECKS- Number of warnings