Skip to content

Profiles

Utsob Roy edited this page Nov 24, 2025 · 4 revisions

Profiles

Profiles allow you to maintain environment-specific configurations within a single dotfiles repository. With profiles, you can easily switch between different setups for work, home, servers, or any other environment without maintaining separate repositories or branches.

Overview

A profile in DotR consists of:

  • Dependencies: Specific packages to deploy when using this profile
  • Variables: Profile-specific variable values that override other sources
  • Prompts: Interactive prompts for environment-specific credentials or values
  • Target Overrides: Alternative deployment destinations for packages

Profiles are especially useful when:

  • You have different configurations for work and personal machines
  • You manage multiple servers with different requirements
  • You want different setups for laptop vs desktop
  • You need environment-specific credentials or settings

Default Profile

DotR automatically creates a default profile when you initialize a new repository with dotr init. This profile is used when:

  • No --profile flag is specified in commands
  • No DOTR_PROFILE environment variable is set

The default profile behaves like any other profile, but with special characteristics:

  • It's created automatically on dotr init
  • It doesn't create target overrides when importing packages
  • It's ideal for packages you want on all machines

Example:

# These both use the default profile
dotr deploy
dotr import ~/.bashrc

# Equivalent to:
dotr deploy --profile default
dotr import ~/.bashrc --profile default

Basic Profile Configuration

Profiles are defined in your config.toml. A default profile is automatically created by dotr init:

# The default profile is created automatically
[profiles.default]
dependencies = []

# Add custom profiles for different environments
[profiles.work]
dependencies = ["f_bashrc", "d_nvim", "f_gitconfig"]

[profiles.work.variables]
GIT_EMAIL = "work@company.com"
WORKSPACE = "~/Work"

[profiles.work.prompts]
VPN_PASSWORD = "Enter your work VPN password"
JIRA_TOKEN = "Enter your Jira API token"

[profiles.home]
dependencies = ["f_bashrc", "d_nvim", "d_gaming"]

[profiles.home.variables]
GIT_EMAIL = "personal@email.com"
WORKSPACE = "~/Projects"

[profiles.home.prompts]
GITHUB_TOKEN = "Enter your GitHub personal access token"

Using Profiles

Deploying with a Profile

# Deploy all packages in the work profile
dotr deploy --profile work

# Deploy all packages in the home profile
dotr deploy --profile home

# Deploy with the default profile (deploys packages in default profile's dependencies)
dotr deploy

# Set environment variable to override which profile is used by default
export DOTR_PROFILE=work
dotr deploy  # Uses work profile instead of default

Importing with a Profile

# Import a file to the default profile
dotr import ~/.bashrc

# Import a file to a specific profile
dotr import ~/.ssh/config --profile work

# With DOTR_PROFILE environment variable
export DOTR_PROFILE=work
dotr import ~/.ssh/config  # Uses work profile

Updating with a Profile

# Update packages for a specific profile
dotr update --profile work

Viewing Variables

# See all variables with a profile applied
dotr print-vars --profile work

# With DOTR_PROFILE environment variable
export DOTR_PROFILE=work
dotr print-vars  # Shows variables for work profile

Environment Variable

You can set the DOTR_PROFILE environment variable to override which profile is used by default. Without this variable, DotR uses the default profile.

# Set in your shell configuration
export DOTR_PROFILE=work

# All commands now use the work profile instead of default
dotr deploy
dotr update
dotr import ~/.config/newfile
dotr print-vars

# Override with --profile flag
dotr deploy --profile home  # Uses home instead of work

This is useful when:

  • You primarily use one specific profile on a machine (not the default)
  • You want to avoid typing --profile repeatedly
  • You're scripting deployments and want consistency

Shell Configuration Example:

# In ~/.bashrc or ~/.zshrc
if [[ "$HOSTNAME" == *"work"* ]]; then
    export DOTR_PROFILE=work
elif [[ "$HOSTNAME" == *"home"* ]]; then
    export DOTR_PROFILE=home
# else: uses default profile
fi

Profile Dependencies

Dependencies specify which packages should be deployed when using a profile. This allows selective deployment based on the environment.

# Default profile - packages everyone gets
[profiles.default]
dependencies = ["f_bashrc", "f_gitconfig"]

# Work-specific packages
[profiles.work]
dependencies = ["f_bashrc", "f_gitconfig", "d_nvim", "f_ssh_config"]

# Home-specific packages
[profiles.home]
dependencies = ["f_bashrc", "f_gitconfig", "d_nvim", "d_gaming"]

# Minimal server setup
[profiles.server]
dependencies = ["f_bashrc", "f_gitconfig", "d_tmux"]

Key Features:

  • Only packages listed in dependencies are deployed
  • Dependencies automatically include any package dependencies
  • Unlisted packages won't deploy unless explicitly specified
  • Use skip = true on packages to only deploy via profiles
  • When you run dotr deploy without --profile, the default profile is used

Profile Variables

Profile variables override package and config variables but not user variables. They're perfect for environment-specific settings.

Variable Precedence

User Variables (.uservariables.toml)  [Highest]
    ↓
Profile Variables (active profile)
    ↓
Package Variables
    ↓
Config Variables
    ↓
Environment Variables                [Lowest]

Example

[variables]
EDITOR = "vim"
THEME = "default"

[profiles.work]
dependencies = ["f_bashrc", "d_nvim"]

[profiles.work.variables]
EDITOR = "nvim"
THEME = "professional"
COMPANY = "Acme Corp"

[profiles.home]
dependencies = ["f_bashrc", "d_nvim"]

[profiles.home.variables]
EDITOR = "nvim"
THEME = "dracula"
PERSONAL_PROJECT = "~/hobby-code"

In your templated configs:

# .bashrc
export EDITOR="{{ EDITOR }}"
export THEME="{{ THEME }}"

{% if COMPANY is defined %}
export COMPANY="{{ COMPANY }}"
{% endif %}

Package Target Overrides

Target overrides allow you to deploy the same package to different locations depending on the active profile.

[packages.ssh_config]
src = "dotfiles/ssh_config"
dest = "~/.ssh/config"  # Default destination

[packages.ssh_config.targets]
work = "~/.ssh/config.work"
server = "/etc/ssh/ssh_config"

When deploying:

# Deploys to ~/.ssh/config.work
dotr deploy --profile work -p ssh_config

# Deploys to /etc/ssh/ssh_config
dotr deploy --profile server -p ssh_config

# Deploys to ~/.ssh/config (default)
dotr deploy -p ssh_config

Skip Flag

The skip flag marks packages to only deploy when explicitly requested or included in a profile's dependencies.

[packages.gaming]
src = "dotfiles/gaming"
dest = "~/.config/gaming"
skip = true  # Only deploy via profile or explicit request

[packages.work_vpn]
src = "dotfiles/vpn"
dest = "~/.config/vpn"
skip = true

[profiles.home]
dependencies = ["gaming"]  # gaming deploys with home profile

[profiles.work]
dependencies = ["work_vpn"]  # work_vpn deploys with work profile

Profile Prompts

Profile prompts allow you to interactively collect environment-specific values when deploying with a profile. This is perfect for credentials, API keys, or any sensitive data that varies between environments.

How Profile Prompts Work

  1. Define prompts in profile: Add a [profiles.name.prompts] section
  2. Deploy with profile: Run dotr deploy --profile name
  3. Interactive input: DotR prompts for missing values
  4. Automatic save: Values are saved to .uservariables.toml
  5. Reuse on next run: Saved values are reused - no re-prompting

Basic Example

[profiles.work]
dependencies = ["f_gitconfig", "f_ssh_config", "d_aws"]

[profiles.work.variables]
WORKSPACE = "~/Work"

[profiles.work.prompts]
WORK_EMAIL = "Enter your work email address"
VPN_PASSWORD = "Enter your work VPN password"
JIRA_TOKEN = "Enter your Jira API token"
AWS_PROFILE = "Enter your AWS profile name (e.g., work-prod)"

[profiles.home]
dependencies = ["f_gitconfig", "f_ssh_config"]

[profiles.home.variables]
WORKSPACE = "~/Projects"

[profiles.home.prompts]
PERSONAL_EMAIL = "Enter your personal email address"
GITHUB_TOKEN = "Enter your GitHub personal access token"

First deployment:

$ dotr deploy --profile work

Enter your work email address
>>> john.doe@company.com

Enter your work VPN password
>>> ********

Enter your Jira API token
>>> abcd1234efgh5678

Enter your AWS profile name (e.g., work-prod)
>>> work-prod

[INFO] Saved prompts to .uservariables.toml
[INFO] Deploying profile 'work'...

Subsequent deployments:

$ dotr deploy --profile work

[INFO] Using saved values from .uservariables.toml
[INFO] Deploying profile 'work'...

Use Cases

Different Credentials per Environment:

[profiles.work.prompts]
GIT_EMAIL = "Enter your work email"
SSH_KEY_PATH = "Enter path to work SSH key"
JIRA_URL = "Enter Jira URL"
JIRA_TOKEN = "Enter Jira API token"

[profiles.home.prompts]
GIT_EMAIL = "Enter your personal email"
SSH_KEY_PATH = "Enter path to personal SSH key"

API Keys and Tokens:

[profiles.production.prompts]
AWS_ACCESS_KEY = "Enter production AWS access key"
AWS_SECRET_KEY = "Enter production AWS secret key"
DATADOG_API_KEY = "Enter Datadog API key"

[profiles.staging.prompts]
AWS_ACCESS_KEY = "Enter staging AWS access key"
AWS_SECRET_KEY = "Enter staging AWS secret key"

Machine-Specific Paths:

[profiles.laptop.prompts]
BACKUP_DIR = "Enter backup directory path"
SYNC_DIR = "Enter sync directory path"

[profiles.desktop.prompts]
BACKUP_DIR = "Enter backup directory path"
SYNC_DIR = "Enter sync directory path"

Prompt Hierarchy

When prompts are defined at multiple levels:

Profile Prompts (active profile)  [Highest]
     ↓
Package Prompts (deployed packages)
     ↓
Config Prompts                    [Lowest]

Profile prompts override config-level prompts. This allows you to:

  • Define common prompts at config level
  • Override them with environment-specific prompts in profiles

Example:

# Config-level prompt (default)
[prompts]
EMAIL = "Enter your email address"

# Work profile overrides with work-specific prompt
[profiles.work.prompts]
EMAIL = "Enter your work email (@company.com)"

# Home profile overrides with personal prompt
[profiles.home.prompts]
EMAIL = "Enter your personal email"

Using Prompted Values in Templates

Prompted values are available in templates just like regular variables:

config.toml:

[profiles.work.prompts]
JIRA_TOKEN = "Enter your Jira API token"
JIRA_URL = "Enter your Jira URL"

Template (dotfiles/f_jira_config):

export JIRA_URL="{{ JIRA_URL }}"
export JIRA_TOKEN="{{ JIRA_TOKEN }}"

Best Practices for Profile Prompts

  1. Use for environment-specific secrets - Different API keys for work, home, staging, production
  2. Clear prompt messages - Include format examples in the prompt text
  3. Group related prompts - Keep all work credentials in work profile, home in home profile
  4. Document expected values - Add comments or README explaining what each prompt needs
  5. Combine with variables - Use variables for non-sensitive, prompts for sensitive data
  6. Test profile switching - Ensure prompts work correctly when switching between profiles

Complete Examples

Work and Home Profiles

banner = true

[variables]
EDITOR = "vim"
SHELL = "bash"

# Default profile - base packages for all machines
[profiles.default]
dependencies = ["f_bashrc", "f_gitconfig"]

# Work profile
[profiles.work]
dependencies = ["f_bashrc", "f_gitconfig", "d_nvim", "f_ssh_config"]

[profiles.work.variables]
GIT_EMAIL = "john.doe@company.com"
GIT_NAME = "John Doe"
THEME = "gruvbox"
WORKSPACE = "~/Work"
SSH_KEY = "~/.ssh/id_ed25519_work"

[profiles.work.prompts]
VPN_PASSWORD = "Enter your work VPN password"
JIRA_TOKEN = "Enter your Jira API token"
CONFLUENCE_TOKEN = "Enter your Confluence API token"

# Home profile
[profiles.home]
dependencies = ["f_bashrc", "f_gitconfig", "d_nvim", "d_gaming"]

[profiles.home.variables]
GIT_EMAIL = "john@personal.com"
GIT_NAME = "John"
THEME = "dracula"
WORKSPACE = "~/Projects"
SSH_KEY = "~/.ssh/id_ed25519"

[profiles.home.prompts]
GITHUB_TOKEN = "Enter your GitHub personal access token"
PERSONAL_API_KEY = "Enter your personal API key"

# Packages
[packages.f_bashrc]
src = "dotfiles/f_bashrc"
dest = "~/.bashrc"

[packages.f_gitconfig]
src = "dotfiles/f_gitconfig"
dest = "~/.gitconfig"

[packages.d_nvim]
src = "dotfiles/d_nvim"
dest = "~/.config/nvim"

[packages.f_ssh_config]
src = "dotfiles/f_ssh_config"
dest = "~/.ssh/config"

[packages.f_ssh_config.targets]
work = "~/.ssh/config.work"

[packages.d_gaming]
src = "dotfiles/d_gaming"
dest = "~/.config/gaming"
skip = true  # Only for home profile

Server Profiles

[profiles.webserver]
dependencies = ["f_bashrc", "d_nginx", "f_ssh_config"]

[profiles.webserver.variables]
SERVER_TYPE = "web"
LOG_DIR = "/var/log/webapp"
DATA_DIR = "/var/www"

[profiles.dbserver]
dependencies = ["f_bashrc", "d_postgres", "f_ssh_config"]

[profiles.dbserver.variables]
SERVER_TYPE = "database"
LOG_DIR = "/var/log/postgres"
DATA_DIR = "/var/lib/postgres"

[packages.d_nginx]
src = "dotfiles/nginx"
dest = "/etc/nginx"
skip = true

post_actions = [
    "nginx -t && systemctl reload nginx || echo 'Nginx config invalid'"
]

[packages.d_postgres]
src = "dotfiles/postgres"
dest = "/etc/postgresql"
skip = true

post_actions = [
    "systemctl reload postgresql"
]

Multi-Environment Development

[profiles.development]
dependencies = ["f_bashrc", "d_nvim", "d_docker", "d_kubernetes"]

[profiles.development.variables]
ENV = "dev"
DEBUG = "true"
API_URL = "http://localhost:3000"

[profiles.staging]
dependencies = ["f_bashrc", "d_docker", "d_kubernetes"]

[profiles.staging.variables]
ENV = "staging"
DEBUG = "false"
API_URL = "https://staging.example.com"

[profiles.production]
dependencies = ["f_bashrc", "d_kubernetes"]

[profiles.production.variables]
ENV = "production"
DEBUG = "false"
API_URL = "https://api.example.com"

[packages.d_kubernetes]
src = "dotfiles/kubernetes"
dest = "~/.kube"

[packages.d_kubernetes.targets]
development = "~/.kube/dev"
staging = "~/.kube/staging"
production = "~/.kube/prod"

Profile-Specific Actions

Actions can use profile variables for environment-specific behavior:

[packages.app_config]
src = "dotfiles/app"
dest = "~/.config/app"

pre_actions = [
    "echo 'Deploying for {{ ENV }} environment'",
    "mkdir -p {{ LOG_DIR }}"
]

post_actions = [
    "{% if ENV == 'production' %}systemctl restart app{% else %}echo 'Dev mode, not restarting'{% endif %}"
]

Combining Profiles with Other Features

Profiles + Templates

[profiles.work]
[profiles.work.variables]
GIT_EMAIL = "work@company.com"
THEME = "professional"

[profiles.home]
[profiles.home.variables]
GIT_EMAIL = "personal@email.com"
THEME = "colorful"

Template (dotfiles/f_gitconfig):

[user]
    email = {{ GIT_EMAIL }}

[core]
    # Work theme is minimal, home theme is colorful
    pager = delta --theme="{{ THEME }}"

Profiles + Package Variables

Package variables still work within profiles:

[profiles.work]
dependencies = ["d_nvim"]

[profiles.work.variables]
COMPANY = "Acme Corp"

[packages.d_nvim.variables]
PLUGIN_MANAGER = "lazy.nvim"
LSP_ENABLED = true

Both COMPANY (profile) and PLUGIN_MANAGER (package) are available in templates.

Profiles + User Variables

User variables override profile variables:

.uservariables.toml:

GIT_EMAIL = "john.doe+personal@company.com"  # Overrides profile

Even with --profile work, this email will be used instead.

Best Practices

  1. Use descriptive profile names: work, home, laptop, server are clear and memorable
  2. Use the default profile for common packages: Put packages needed everywhere in the default profile
  3. Keep profile dependencies minimal: Only include what's needed for that environment
  4. Use profile variables for credentials: Different emails, API keys, SSH keys per environment
  5. Leverage target overrides: Deploy configs to different paths per environment
  6. Use skip flag wisely: Mark environment-specific packages with skip = true
  7. Document profiles: Add comments explaining what each profile is for
  8. Test profile switching: Verify templates render correctly with each profile
  9. Use profiles with actions: Different actions for different environments
  10. Combine with .uservariables.toml: Machine-specific overrides still work
  11. Keep base config minimal: Put environment-specific stuff in profiles
  12. Use DOTR_PROFILE environment variable: Set it on machines where you want to use a specific profile instead of default

Workflow Examples

Setting Up a New Work Machine

# Clone your dotfiles
git clone https://github.com/user/dotfiles.git ~/dotfiles
cd ~/dotfiles

# Deploy work profile directly
dotr deploy --profile work

# Or set DOTR_PROFILE to always use work profile on this machine
echo 'export DOTR_PROFILE=work' >> ~/.bashrc
source ~/.bashrc

# Now deploy uses work profile automatically
dotr deploy  # Uses work profile from DOTR_PROFILE

Switching Between Environments

# Deploy work configuration
dotr deploy --profile work

# Later, switch to home configuration
dotr deploy --profile home

Managing Multiple Servers

# On web server
dotr deploy --profile webserver

# On database server
dotr deploy --profile dbserver

# On monitoring server
dotr deploy --profile monitoring

Development Workflow

# Local development
dotr deploy --profile development

# Deploy to staging
dotr deploy --profile staging

# Deploy to production
dotr deploy --profile production

Troubleshooting

Profile Not Found

Error: Profile 'myprofile' not defined

Solution: Verify the profile exists in config.toml:

[profiles.myprofile]
dependencies = [...]

Package Not Deploying with Profile

  • Check the profile's dependencies array includes the package
  • Verify package name matches exactly
  • If using skip = true, package must be in profile dependencies

Variables Not Overriding

  • Check variable precedence: user > profile > package > config > environment
  • Use dotr print-vars --profile name to see actual values
  • Verify profile name is correct when deploying

Target Override Not Working

[packages.mypackage.targets]
myprofile = "~/alt/path"  # Profile name must match exactly

Ensure you're using the correct profile name: dotr deploy --profile myprofile

Advanced Usage

Conditional Profile Includes

Use templates to check which profile is active:

{% if ENV == "work" %}
export WORK_SPECIFIC_VAR="value"
{% elif ENV == "home" %}
export HOME_SPECIFIC_VAR="value"
{% endif %}

Dynamic Profile Selection

Create a wrapper script or use environment variable:

Using DOTR_PROFILE:

# In ~/.bashrc or ~/.zshrc
if [[ "$HOSTNAME" == *"work"* ]]; then
    export DOTR_PROFILE=work
elif [[ "$HOSTNAME" == *"home"* ]]; then
    export DOTR_PROFILE=home
else
    export DOTR_PROFILE=server
fi

# Now just run: dotr deploy

Using a wrapper script:

#!/bin/bash
# deploy.sh

if [[ "$HOSTNAME" == *"work"* ]]; then
    dotr deploy --profile work
elif [[ "$HOSTNAME" == *"home"* ]]; then
    dotr deploy --profile home
else
    dotr deploy --profile server
fi

Profile Inheritance (via dependencies)

While profiles don't inherit directly, you can share packages across profiles. The default profile is a good place to put common packages:

# Common packages all machines get
[profiles.default]
dependencies = ["f_bashrc", "f_gitconfig"]

# Work adds work-specific packages
[profiles.work]
dependencies = ["f_bashrc", "f_gitconfig", "work_vpn"]

# Home adds home-specific packages
[profiles.home]
dependencies = ["f_bashrc", "f_gitconfig", "gaming"]

Note that profiles don't automatically inherit from default - you need to explicitly list shared packages in each profile's dependencies.

Further Reading

Clone this wiki locally