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
- 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.
- Verify with: docker exec coolify touch /data/coolify/ssh/.probe (should succeed silently).
- 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
- After install, restore, or host migration, proactively run the chown 9999 / chmod 700 command before uploading keys.
- The container user is fixed at uid 9999 — any volume bind for /data/coolify/ssh must honor that.
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
- Failed to create SSH keys storage directory
- Failed to store SSH key: {message}
- Failed to open lock file for SSH key: {$lockFile}
- Failed to write SSH key to filesystem. Check disk space and
- Failed to acquire lock for SSH key: {$keyLocation}
AI-assisted analysis of coollabsio/coolify@70b9acc424 (2026-08-17).
Data as JSON: /api/errors/ce05a6ff613b0f19.
Report an issue: GitHub.