windmill-labs/windmill · critical

could not create initial server dir

Error message

could not create initial server dir

What it means

At server startup, windmill_main creates the base WINDMILL_DIR directory (usually /tmp/windmill) recursively with tokio's DirBuilder and .expect()s success. If creation fails, the process panics with this message — the server cannot run without its base directory for job files, caches, and storage.

Source

Thrown at backend/src/main.rs:1232

        send_logs_to_object_store(&conn, &hostname, &mode);

        #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
        if !worker_mode {
            monitor_mem().await;
        }

        let addr = SocketAddr::from((server_bind_address, port));
        let listener = tokio::net::TcpListener::bind(addr)
            .await
            .context("binding main windmill server")?;

        let (base_internal_tx, base_internal_rx) = tokio::sync::oneshot::channel::<String>();

        DirBuilder::new()
            .recursive(true)
            .create(&*WINDMILL_DIR)
            .expect("could not create initial server dir");

        #[cfg(feature = "tantivy")]
        let should_index_jobs = mode == Mode::Indexer || mode_and_addons.indexer;

        #[cfg(feature = "tantivy")]
        if should_index_jobs {
            if let Some(db) = conn.as_sql() {
                reload_indexer_config(&db).await;
            }
        }

        #[cfg(feature = "tantivy")]
        let (index_reader, index_writer) = if should_index_jobs {
            if let Some(db) = conn.as_sql() {
                let mut indexer_rx = killpill_rx.resubscribe();

                let (mut reader, mut writer) = (None, None);
                tokio::select! {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Check the panic's underlying io error in logs; verify the parent of WINDMILL_DIR (default /tmp/windmill) is writable by the server user: `ls -ld /tmp /tmp/windmill`.
  2. Remove or rename any non-directory file occupying the path.
  3. Point WINDMILL_PATH env at a writable location (e.g. a persistent volume mount) if /tmp is read-only.
  4. Fix container/security-manager settings (drop read-only rootfs/ProtectTmp restrictions) or run as a user with write access.

Example fix

// before (panic on failure)
DirBuilder::new().recursive(true).create(&*WINDMILL_DIR)
    .expect("could not create initial server dir");
// after (fail with context instead of bare panic)
DirBuilder::new().recursive(true).create(&*WINDMILL_DIR)
    .await
    .unwrap_or_else(|e| panic!("could not create initial server dir {}: {e}", *WINDMILL_DIR));
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: verify the base dir can be created before starting the server
let dir = std::path::Path::new(&*WINDMILL_DIR);
if let Some(parent) = dir.parent() {
    let md = std::fs::metadata(parent)?;
    if md.permissions().readonly() {
        return Err(format!("{} is not writable; set WINDMILL_PATH to a writable location", parent.display()));
    }
}

Try / catch

// Startup panics via expect; wrap pre-checks in your launcher/container entrypoint:
match std::fs::create_dir_all(&*WINDMILL_DIR) {
    Ok(_) => {},
    Err(e) => {
        eprintln!("could not create initial server dir {}: {e}", *WINDMILL_DIR);
        std::process::exit(1);
    }
}

Prevention

When it happens

Trigger: Starting the windmill server binary where creating WINDMILL_DIR fails: the parent path is not writable by the server user, a non-directory file already occupies the path, read-only root filesystem, or restrictive container volume permissions.

Common situations: Docker/Kubernetes containers running as non-root with read-only or unwritable /tmp; systemd service with a hardened ProtectTmp/ReadOnlyPaths; WINDMILL_PATH/WINDMILL_DIR env pointing into a mounted file or missing volume.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/f0cc5a216c75e64f. Report an issue: GitHub.