# How CasaOS Implements Custom Startup Scripts in the start.d Directory

> Learn how CasaOS runs custom startup scripts in start.d. Discover executable files, execute them lexicographically, and handle failures gracefully. Optimize your CasaOS experience.

- Repository: [IceWhale/CasaOS](https://github.com/IceWhaleTech/CasaOS)
- Tags: how-to-guide
- Published: 2026-06-26

---

**CasaOS executes user-provided startup scripts from the `start.d` directory by resolving the configuration path, discovering executable files, and running them in lexicographic order via `exec.CommandContext` while logging output and continuing past individual failures.**

CasaOS supports extensibility through custom startup scripts placed in a special `start.d` directory, allowing users to run initialization logic without modifying core server code. This mechanism follows Unix rc.d conventions and is implemented in the IceWhaleTech/CasaOS repository through a dedicated execution pipeline. When the CasaOS service starts, it automatically discovers and runs these scripts after binding the HTTP server but before notifying systemd that the service is ready.

## Resolving the start.d Directory Path

CasaOS determines the location of the startup script directory by joining the base configuration path with the subdirectory name `start.d`. In [`main.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/main.go) (lines 200-203), the server constructs this path using the `constants.DefaultConfigPath` constant defined in [`internal/conf/var.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/internal/conf/var.go).

```go
scriptDirectory := filepath.Join(constants.DefaultConfigPath, "start.d")

```

The `DefaultConfigPath` typically resolves to `/etc/casaos` or `$HOME/.config/casaos` depending on the installation context. This absolute path is then passed to the execution helper to begin the script discovery process.

## The Script Execution Pipeline

The `ExecuteScripts` function in [`internal/command/execute.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/internal/command/execute.go) orchestrates the discovery and execution of custom startup scripts. This implementation provides a robust, fault-tolerant mechanism for running user-provided initialization code.

### Script Discovery and Ordering

The function reads all entries in the `start.d` directory, filters out non-executable files, and sorts the remaining entries lexicographically. This deterministic ordering ensures scripts run in a predictable sequence based on filename (e.g., [`01-init.sh`](https://github.com/IceWhaleTech/CasaOS/blob/main/01-init.sh) before [`02-services.sh`](https://github.com/IceWhaleTech/CasaOS/blob/main/02-services.sh)).

### Process Spawning and Environment Inheritance

For each discovered script, CasaOS spawns a new process using `exec.CommandContext`. The child process inherits the CasaOS environment variables, and both `stdout` and `stderr` streams are piped directly to the CasaOS logger for centralized observability.

### Error Handling Strategy

If a script exits with a non-zero status code, the error is logged but the execution loop continues. This design prevents a single failing script from blocking subsequent initialization steps or preventing the CasaOS service from starting.

## Execution Timing and Service Lifecycle

The call to `command.ExecuteScripts(scriptDirectory)` occurs at a specific point in the boot sequence:

```go
// In main.go - after HTTP server binds, before systemd notification
command.ExecuteScripts(scriptDirectory)

```

This timing is critical: scripts run **after** the HTTP server has bound to its port and the service URL file (`casaos.url`) has been written, but **before** the system notifies systemd that the service is ready. Consequently, startup scripts can safely interact with the running CasaOS instance to register additional routes, mount external drives, or launch background daemons.

## Creating Custom Startup Scripts

Any executable file placed in the `start.d` directory will be executed. CasaOS does not restrict scripts to specific languages, supporting shell scripts, compiled binaries, Python programs, or any other executable format.

### Bash Script Example

Create a shell script to mount an external drive at startup:

```sh

# ~/casaos-config/start.d/01-mount-external.sh

#!/usr/bin/env bash

# Example: mount an external drive at startup

mount /dev/sdb1 /mnt/external && echo "External drive mounted"

```

Make the script executable:

```bash
chmod +x ~/casaos-config/start.d/01-mount-external.sh

```

### Compiled Go Binary Example

You can also use compiled languages. This Go program performs cleanup operations:

```go
// ~/casaos-config/start.d/02-cleanup.go
package main

import (
    "log"
    "os"
)

func main() {
    log.Println("Running custom cleanup...")
    // custom logic here
    _ = os.RemoveAll("/tmp/casaos-temp")
}

```

Build and mark as executable:

```bash
go build -o ~/casaos-config/start.d/02-cleanup ~/casaos-config/start.d/02-cleanup.go
chmod +x ~/casaos-config/start.d/02-cleanup

```

When CasaOS starts, it executes [`01-mount-external.sh`](https://github.com/IceWhaleTech/CasaOS/blob/main/01-mount-external.sh) followed by `02-cleanup` based on lexicographic ordering.

## Summary

- CasaOS resolves the startup script path by joining `constants.DefaultConfigPath` with `"start.d"` in [`main.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/main.go).
- The `ExecuteScripts` function in [`internal/command/execute.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/internal/command/execute.go) handles discovery, sorting, and execution of all executable files in the directory.
- Scripts run in lexicographic order after the HTTP server binds but before systemd receives the ready notification.
- Non-zero exit codes are logged but do not halt the execution of subsequent scripts.
- Any executable format is supported, including Bash scripts and compiled Go binaries.

## Frequently Asked Questions

### What file permissions are required for scripts in the start.d directory?

Scripts must be marked as executable (typically `chmod +x`). The `ExecuteScripts` function explicitly filters out non-executable files during the discovery phase, so files without executable permissions will be ignored even if they contain valid code.

### What happens if a startup script fails or returns an error?

CasaOS logs the error and continues executing the remaining scripts. The error handling logic in [`internal/command/execute.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/internal/command/execute.go) ensures that a failing script does not prevent other initialization routines from running or block the CasaOS service from completing its startup sequence.

### Can I use programming languages other than Bash for startup scripts?

Yes. CasaOS treats any executable file as a valid startup script. You can use Python, Go, Rust, Node.js, or any other language provided the file has the executable bit set and includes the appropriate shebang line (for interpreted languages) or is compiled to a native binary.

### Where is the start.d directory located on my system?

The location depends on your CasaOS configuration. According to [`internal/conf/var.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/internal/conf/var.go), the path is constructed by appending `start.d` to `constants.DefaultConfigPath`, which typically resolves to `/etc/casaos/start.d` for system-wide installations or `~/.config/casaos/start.d` for user-specific configurations. Check your `conf/conf.conf.sample` for the exact path in your deployment.