How to Write a Migration Script in the Migrations/ Directory with the Timestamp Naming Convention
To write a migration script in Omarchy's migrations/ directory, create a file named with a Unix timestamp followed by .sh (e.g., 1788862626.sh), start with a descriptive echo line, write idempotent bash logic without a shebang, and ensure the file permissions are set to 0644.
Omarchy uses migration scripts to perform one-off repairs when package updates need to modify state that pacman cannot handle automatically. When you write a migration script in the migrations/ directory according to the omac/omarchy source code, you must follow a strict timestamp naming convention and formatting requirements so the omarchy-migrate runner can discover and execute it reliably.
Timestamp Naming Convention
Every migration file must reside in the repository's migrations/ directory and use a Unix timestamp (seconds since epoch) as its filename with the .sh extension.
For example:
migrations/1788862626.sh
This timestamp guarantees total ordering and prevents name collisions across concurrent development. The omarchy-dev-add-migration helper automates this naming by extracting the current commit date in Unix format and creating the file for you, as implemented in /bin/omarchy-dev-add-migration lines 30-36.
Required File Format and Structure
The migration runner executes scripts with bash -euo pipefail, imposing specific formatting constraints documented in agents/skills/migrations.md.
Permissions and Shebang
Migration files must have permissions set to 0644 (-rw-r--r--). The runner does not rely on executable bits. Crucially, do not include a shebang line—the migration runner supplies the interpreter explicitly.
Header and Variable Usage
Begin each script with a single echo line that briefly describes the migration's purpose. For any repository-relative paths, use the $OMARCHY_PATH variable instead of hardcoded paths.
Idempotence Requirements
All migration actions must be safe to run repeatedly. Check existing system state before modifying it so the script can be re-executed without side effects. For example, verify a symlink exists before recreating it, or check if a package is present before installing.
Preferred Helper Commands
When possible, use Omarchy helper utilities such as omarchy-cmd-present, omarchy-pkg-add, and other standardized tools for common operations rather than raw shell commands.
Practical Example
Here is a real-world migration from migrations/1788862626.sh that relinks a Neovim theme configuration:
# Example migration demonstrating idempotent symlink handling
echo "Relink Neovim theme to Omarchy current state"
theme_link="$HOME/.config/nvim/lua/plugins/theme.lua"
current_target="../../../../.local/state/omarchy/current/theme/neovim.lua"
# Exit early if the symlink does not exist (idempotent check)
[[ -L $theme_link ]] || exit 0
# Re-create the symlink atomically
ln -sfn "$current_target" "$theme_link"
This pattern ensures the script exits successfully if no action is needed and safely updates the link when required.
Step-by-Step Creation Process
While you can manually create files, the recommended workflow uses the provided helper to ensure correct naming and location.
-
Generate the file using the helper:
omarchy-dev-add-migration --no-editThis creates
migrations/<unix-timestamp>.shwith the correct permissions and prints the absolute path. Omit--no-editto open the file immediately innvim. -
Add the descriptive header:
Insert an
echoline describing the migration's purpose as the first executable statement. -
Implement idempotent logic:
Write bash code that checks state before modifying, using
$OMARCHY_PATHfor repository references and preferring Omarchy helper commands. -
Verify permissions:
Ensure the file mode is
0644(though the helper sets this by default). -
Test locally:
Validate the migration against a temporary home directory to mimic the runner environment:
HOME=$(mktemp -d) bash -euo pipefail migrations/<timestamp>.shThis mimics the execution environment used by
omarchy-migrateas documented inagents/skills/migrations.mdlines 51-58.
How Migration Execution Works
Understanding the runtime behavior helps ensure your scripts handle edge cases correctly.
- During package updates: After
omarchy updatecompletes,omarchy-migrateruns all pending migrations for the current user. - At graphical login: A systemd service triggers
omarchy-migrate --pendingand presents a terminal if any migrations remain unexecuted. - Manual invocation: Users can run
omarchy-migratedirectly at any time; completed migrations are skipped automatically.
Completion tracking occurs per-user under ~/.local/state/omarchy/migrations/<migration-filename>, ensuring each user on a machine executes the script independently.
Summary
- Timestamp naming: Use Unix epoch seconds with
.shextension (e.g.,1788862626.sh) in themigrations/directory. - No shebang required: The runner supplies
bash -euo pipefail; omit the interpreter declaration. - Start with echo: Begin with a descriptive
echoline explaining the migration's purpose. - Idempotence is mandatory: Check state before modifying to allow safe re-execution.
- Use helpers: Leverage
omarchy-dev-add-migrationfor creation and Omarchy utility commands for operations. - Permissions: Set files to
0644(readable, not executable).
Frequently Asked Questions
Why does Omarchy use Unix timestamps instead of sequential numbers for migration naming?
Unix timestamps provide a decentralized naming scheme that prevents collisions when multiple developers create migrations simultaneously without coordinating sequence numbers. The numeric value ensures chronological ordering while avoiding merge conflicts that sequential numbering (001, 002) typically creates in distributed development.
What happens if my migration script fails during execution?
The omarchy-migrate runner executes scripts with bash -euo pipefail, meaning it exits immediately on errors or undefined variables. If a migration fails, it will not be marked as complete in ~/.local/state/omarchy/migrations/, causing it to retry on the next update or login until it succeeds or is manually resolved.
Can I use languages other than Bash for migration scripts?
No. The migration runner specifically expects Bourne-again shell (bash) scripts and invokes them with explicit bash arguments. While you could theoretically call other interpreters from within the bash script, the migration file itself must be valid bash to satisfy the runner's execution model.
How do I test a migration script without affecting my live system?
Create a temporary home directory and execute the script manually with the same flags the runner uses: HOME=$(mktemp -d) bash -euo pipefail migrations/<timestamp>.sh. This isolates the migration's effects while accurately simulating the environment that omarchy-migrate provides during actual execution.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →