coollabsio/coolify · error · Exception

SSH keys storage directory is not writable. Run on the host:

Error message

SSH keys storage directory is not writable. Run on the host: sudo chown -R 9999 /data/coolify/ssh && sudo chmod -R 700 /data/coolify/ssh && docker restart coolify

What it means

Thrown by PrivateKey::ensureStorageDirectoryExists() (app/Models/PrivateKey.php:285). After ensuring the directory exists, the routine proves writability by writing a probe file .test_write_{uniqid} into the ssh-keys disk; if put() returns false, storage is unusable and the exception embeds the exact host-side remediation command. This is the most common SSH-key storage failure on self-hosted installs: the Coolify container runs as uid 9999 and the host directory is owned by root.

Source

Thrown at app/Models/PrivateKey.php:285

    protected function ensureStorageDirectoryExists()
    {
        $disk = Storage::disk('ssh-keys');
        $directoryPath = '';

        if (! $disk->exists($directoryPath)) {
            $success = $disk->makeDirectory($directoryPath);
            if (! $success) {
                throw new \Exception('Failed to create SSH keys storage directory');
            }
        }

        // Check if directory is writable by attempting a test file
        $testFilename = '.test_write_'.uniqid();
        $testSuccess = $disk->put($testFilename, 'test');

        if (! $testSuccess) {
            throw new \Exception('SSH keys storage directory is not writable. Run on the host: sudo chown -R 9999 /data/coolify/ssh && sudo chmod -R 700 /data/coolify/ssh && docker restart coolify');
        }

        // Clean up test file
        $disk->delete($testFilename);
    }

    public function getKeyLocation()
    {
        return Storage::disk('ssh-keys')->path("ssh_key@{$this->uuid}");
    }

    public function updatePrivateKey(array $data)
    {
        return DB::transaction(function () use ($data) {
            $this->update($data);

            return $this;
        });

View on GitHub (pinned to 70b9acc424)

Solutions

  1. Run exactly what the message says on the host: sudo chown -R 9999 /data/coolify/ssh && sudo chmod -R 700 /data/coolify/ssh && docker restart coolify.
  2. Verify with: docker exec coolify touch /data/coolify/ssh/.probe (should succeed silently).
  3. Retry the SSH key operation in the UI/API.
Defensive patterns

Strategy: validation

Validate before calling

// Same pre-flight as callers can run
$root = Storage::disk('ssh-keys')->path('');
$probe = $root.'/.probe_'.uniqid();
if (@file_put_contents($probe, 'x') === false) {
    throw new \RuntimeException('Run on the host: sudo chown -R 9999 /data/coolify/ssh && sudo chmod -R 700 /data/coolify/ssh && docker restart coolify');
}
@unlink($probe);

Try / catch

try {
    $privateKey->storeInFileSystem();
} catch (\Exception $e) {
    if (str_contains($e->getMessage(), 'storage directory is not writable')) {
        // apply the exact host command from the message, restart coolify, retry
    }
    throw $e;
}

Prevention

When it happens

Trigger: Any PrivateKey create/update/delete that hits the filesystem when /data/coolify/ssh exists but uid 9999 cannot write into it (root-owned dir, 755 perms, wrong volume bind) — the probe write fails before any key operation starts.

Common situations: Post-install first key upload; permission drift after restoring a backup or moving the host; bind-mounting the wrong directory over /data/coolify/ssh.

Related errors


AI-assisted analysis of coollabsio/coolify@70b9acc424 (2026-08-17). Data as JSON: /api/errors/ce05a6ff613b0f19. Report an issue: GitHub.