diff --git a/Installer/Distribution/Defaults/deploy.php b/Installer/Distribution/Defaults/deploy.php
deleted file mode 100644
index 93c2b1e..0000000
--- a/Installer/Distribution/Defaults/deploy.php
+++ /dev/null
@@ -1,5 +0,0 @@
- $pwd])) {
+        writeln('');
+        if (!askConfirmationInput('There are no files staged. Do you want to add all files?')) {
+            exit(1);
+        }
+        runLocally('git add .');
+    }
+
+    $crop = 100;
+    writebox(
+        "Line 1 will be cropped at $crop characters. 
All other lines will be wrapped after $crop characters.",
+        null
+    );
+    // Cropping is index based, so we need to add 2
+    $crop = $crop + 2;
+
+    $type = askChoiceln(
+        'Select the type of change that you\'re committing',
+        $types,
+        array_key_first($types)
+    );
+    writeCleanLine();
+    $type .= ':';
+    $short = askln('Write a short, imperative tense description of the change:', true, null, false, $type);
+    $long = askln('Provide a longer description of the change: (press enter to skip)');
+
+    $breaking = null;
+    if ($type !== 'Breaking:') {
+        $breaking = askConfirmationInput('Are there any breaking changes?', 'Describe the breaking changes:');
+    }
+    $issues = askConfirmationInput('Does this change affect any open issues?', 'Add issue references (e.g. "fix #123", "re #123".):');
+
+    $output = substr("$type $short", 0, $crop);
+    if (is_string($long)) {
+        $output .= "\n\n" . wordwrap($long, $crop);
+    }
+    if (is_string($breaking)) {
+        $output .= "\n\n" . wordwrap("BREAKING CHANGE: $breaking", $crop);
+    }
+    if (is_string($issues)) {
+        $output .= "\n\n" . wordwrap($issues, $crop);
+    }
+    $output = str_replace('"', '\"', $output);
+    runLocally("git commit -m \"$output\"", ['cwd' => $pwd]);
+
+    writeCleanLine();
+})->once();
+
+desc('Create release tag on git');
+task('git:tag', static function (): void {
+    $latest = 'git describe --tags $(git rev-list --tags --max-count=1)';
+    if (!testLocally($latest . '>/dev/null 2>&1')) {
+        $tag = askln('Please enter a tag name', true);
+    } else {
+        $latest = runLocally($latest);
+        $type = askChoiceln("Latest tag is $latest. Choose type of release", [
+            "Patch",
+            "Minor",
+            "Major"
+        ]);
+        $prefix = '';
+        // Check if $latest starts with an "v"
+        if (strpos($latest, 'v') === 0) {
+            $prefix = 'v';
+            $latest = substr($latest, 1);
+        }
+
+        $split = explode('.', $latest);
+        $patch = (int) array_pop($split);
+        $minor = (int) array_pop($split);
+        $major = (int) array_pop($split);
+
+        switch ($type) {
+            case 'Patch':
+                $patch++;
+                break;
+            case 'Minor':
+                $patch = 0;
+                $minor++;
+                break;
+            case 'Major':
+                $patch = 0;
+                $minor = 0;
+                $major++;
+                break;
+        }
+        $tag = sprintf('%s%d.%d.%d', $prefix, $major, $minor, $patch);
+    }
+
+    $gh = get('local/gh');
+
+    $date = (new DateTime('now', new DateTimeZone('UTC')))->format(DateTime::RFC850);
+    $description = askln(
+        'Add a description for the tag',
+        false,
+        "Deployment on $date"
+    );
+    runLocally("git tag -a -m '$description' '$tag'");
+    runLocally('git push origin --tags');
+    if ($gh) {
+        runLocally("$gh release create $tag --generate-notes");
+    }
+})->once();
+
+desc('Set the know host for the SSH_KNOWN_HOSTS secret');
+task('git:ssh:know_hosts', static function (): void {
+    $gh = get('local/gh');
+    secretFeedback('SSH_KNOWN_HOSTS', (bool)$gh);
+    $keys = '';
+    foreach (getAllHostnames() as $hostname) {
+        $key = trim(runLocally("ssh-keyscan $hostname"));
+        $keys .= $key;
+        writeCleanLine("$key");
+    }
+    var_dump(timestamp());
+
+    if ($gh) {
+        //   runLocally("$gh secret set SSH_KNOWN_HOSTS --body '$keys'");
+    }
+})->once();
+
+desc('Set the private key for SSH_PRIVATE_KEY secret and upload public key to host');
+task('git:ssh:key', static function (): void {
+    $gh = get('local/gh');
+    runLocally('ssh-keygen -q -t rsa -b 4096 -N "" -C "Github Actions" -f __temp_ssh_key');
+    $private = runLocally('cat __temp_ssh_key');
+    $public = runLocally('cat __temp_ssh_key.pub');
+    secretFeedback('SSH_PRIVATE_KEY', (bool)$gh);
+    if ($gh) {
+        runLocally("$gh secret set SSH_PRIVATE_KEY --body '$private'");
+    }
+    writeln("$private");
+    on(Deployer::get()->hosts, function () use ($public) {
+        $file = run("cat ~/.ssh/authorized_keys");
+        if (strpos($file, $public) === false) {
+            run("echo '$public' >> ~/.ssh/authorized_keys");
+            writebox('The public key was added to authorized_keys on the host {{alias}}');
+        }
+    });
+    runLocally('rm -f __temp_ssh_key __temp_ssh_key.pub');
+})->once();
+
+/**
+ * @param string $key
+ * @param boolean $hasGithubCLI
+ * @return void
+ */
+function secretFeedback(string $key, bool $hasGithubCLI): void
+{
+    if ($hasGithubCLI) {
+        writebox("Follow entry was made as $key secret on github:");
+        return;
+    }
+    writebox("Put this value as $key secret on github:");
+}
diff --git a/Task/Server.php b/Task/Server.php
new file mode 100644
index 0000000..5c074a3
--- /dev/null
+++ b/Task/Server.php
@@ -0,0 +1,165 @@
+Set PHP version on uberspace
Current version:
$currentVersion");
+
+    $availableVersions = run('uberspace tools version list php');
+    $versionsList = explode(\PHP_EOL, str_replace('- ', '', $availableVersions));
+    $version = askChoiceln('Please choose the desired version', $versionsList);
+
+    if ($version !== $currentVersionShort) {
+        $output = run("uberspace tools version use php $version");
+        writebox($output, 'bgSuccess');
+        return;
+    }
+    writebox("As PHP is already set to $version,
no configuration was changed");
+});
+
+desc('Add a domain to uberspace');
+task('server:domain:add', static function (): void {
+    writebox('Get list of domains, please wait...', null);
+    $currentEntry = run('uberspace web domain list');
+    $alias = parse('alias');
+    writebox("Add a domain to uberspace
+If you have multiple domains, you will be asked
+after every entry if you wand to add another domain.
+
+Current entry:
+$currentEntry
+
+To cancel enter exit as answer");
+    // Check if the alias seems to have a subdomain
+    $wwwDomain = 'www.' . $alias;
+    $defaultDomain = substr_count($alias, '.') > 1 ? $alias : $wwwDomain;
+    $suggestions = [$alias, $wwwDomain];
+    $firstDomain = askDomain('Please enter the domain', $defaultDomain, $suggestions);
+    if ($firstDomain === 'exit') {
+        return;
+    }
+    $domains = [
+        $firstDomain
+    ];
+    writeln('');
+    while ($domain = askDomain('Please enter another domain or press enter to continue', null, $suggestions)) {
+        if ($domain === 'exit') {
+            return;
+        }
+        if ($domain) {
+            $domains[] = $domain;
+        }
+        writeln('');
+    }
+    $outputDomains = implode("\n", $domains);
+    $ip = '';
+    writebox('Adding domains, please wait...');
+    foreach ($domains as $domain) {
+        $ip = run("uberspace web domain add $domain");
+    }
+    writebox("Following entries are added:
$outputDomains
$ip", 'green');
+});
+
+desc('Remove a domain from uberspace');
+task('server:domain:remove', static function (): void {
+    writebox('Get list of domains, please wait...', null);
+    $currentEntry = run('uberspace web domain list');
+
+    if ($currentEntry == parse('{{remote_user}}.uber.space')) {
+        writebox('No domains to remove');
+        return;
+    }
+
+    writebox("Remove a domain from uberspace
+If you have multiple domains, you will be asked
+after every entry if you wand to remove another domain.
+
+Current entry:
+$currentEntry
+
+To finish the setup, press enter or choose the last entry");
+
+    $currentEntriesArray = explode(\PHP_EOL, $currentEntry);
+    $currentEntriesArray[] = 'Finish setup';
+    $domains = [];
+
+    while ($domain = askChoiceln('Please choose the domain you want to remove', $currentEntriesArray, sizeof($currentEntriesArray) - 1)) {
+        if ($domain === 'Finish setup') {
+            break;
+        }
+        $currentEntriesArray = array_values(array_diff($currentEntriesArray, [$domain]));
+        $domains[] = $domain;
+    }
+    if (sizeof($domains)) {
+        $outputDomains = implode("\n", $domains);
+        writebox('Removing domains, please wait...');
+        foreach ($domains as $domain) {
+            run("uberspace web domain del $domain");
+        }
+        writebox('Following entries are removed:
' . $outputDomains, 'bgSuccess');
+    } else {
+        writebox('No Domains where removed', 'error');
+    }
+});
+
+desc('List all domains and subdomains');
+task('server:domain:list', static function (): void {
+    writebox('Get list of domains, please wait...', null);
+    $currentEntry = run('uberspace web domain list');
+    writebox("Registered domains on the uberspace account \"{{remote_user}}\"
+
+$currentEntry");
+});
+
+desc('Create and/or read the deployment key');
+task('server:ssh_key', static function (): void {
+    if (!test('[ -f ~/.ssh/{{ssh_key}}.pub ]')) {
+        // -q Silence key generation
+        // -t Set algorithm
+        // -b Specifies the number of bits in the key
+        // -N Set the passphrase
+        // -C Comment for the key
+        // -f Specifies name of the file in which to store the created key
+        run('cat /dev/zero | ssh-keygen -q -t rsa -b 4096 -N "" -C "$(hostname -f)" -f ~/.ssh/{{ssh_key}}');
+    }
+
+    // We dont use `ssh-keygen -y` because we also want to output the comment
+    writebox('The public key ({{ssh_key}}.pub) from {{alias}}  is:');
+    writeln('' . run('cat ~/.ssh/{{ssh_key}}.pub') . '');
+    writeln('');
+
+    $sshKnowHostsFile = '~/.ssh/known_hosts';
+    $repoHost = get('repository_url_parts')['host'];
+
+    if (!test("grep -q '$repoHost' $sshKnowHostsFile")) {
+        run("ssh-keyscan $repoHost >> $sshKnowHostsFile");
+    }
+});
diff --git a/Task/Slack.php b/Task/Slack.php
new file mode 100644
index 0000000..87fc04c
--- /dev/null
+++ b/Task/Slack.php
@@ -0,0 +1,84 @@
+ get('slack_title'),
+        'text' => get('slack_text'),
+        'color' => get('slack_color'),
+        'mrkdwn_in' => ['text'],
+    ];
+
+    foreach (array_unique($slackWebhook) as $hook) {
+        Httpie::post($hook)->body(['attachments' => [$attachment]])->send();
+    }
+})->once()->hidden();
+
+desc('Notifying Slack about deploy finish');
+task('slack:notify:success', static function (): void {
+    $slackWebhook = get('slack_webhook', false);
+    if (!$slackWebhook) {
+        return;
+    }
+    if (!is_array($slackWebhook)) {
+        $slackWebhook = [$slackWebhook];
+    }
+
+    $attachment = [
+        'title' => get('slack_title'),
+        'text' => get('slack_success_text'),
+        'color' => get('slack_success_color'),
+        'mrkdwn_in' => ['text'],
+    ];
+
+    foreach (array_unique($slackWebhook) as $hook) {
+        Httpie::post($hook)->body(['attachments' => [$attachment]])->send();
+    }
+})->once()->hidden();
+
+
+desc('Notifying Slack about deploy failure');
+task('slack:notify:failure', static function (): void {
+    $slackWebhook = get('slack_webhook', false);
+    if (!$slackWebhook) {
+        return;
+    }
+    if (!is_array($slackWebhook)) {
+        $slackWebhook = [$slackWebhook];
+    }
+
+    $attachment = [
+        'title' => get('slack_title'),
+        'text' => get('slack_failure_text'),
+        'color' => get('slack_failure_color'),
+        'mrkdwn_in' => ['text'],
+    ];
+
+    foreach (array_unique($slackWebhook) as $hook) {
+        Httpie::post($hook)->body(['attachments' => [$attachment]])->send();
+    }
+})->once()->hidden();
+
+after('deploy:failed', 'slack:notify:failure');
diff --git a/Utility/Hostname.php b/Utility/Hostname.php
new file mode 100644
index 0000000..14d3589
--- /dev/null
+++ b/Utility/Hostname.php
@@ -0,0 +1,50 @@
+ 1 ? $alias : 'www.' . $alias;
+}
+
+/**
+ * Returns all domains
+ *
+ * @return array
+ */
+function getAllDomains(): array
+{
+    $hostnames = [];
+    on(Deployer::get()->hosts, function () use (&$hostnames) {
+        $hostnames[] = get('alias');
+    });
+    return array_unique($hostnames);
+}
+
+/**
+ * Returns all hostnames
+ *
+ * @return array
+ */
+function getAllHostnames(): array
+{
+    $hostnames = [];
+    on(Deployer::get()->hosts, function ($host) use (&$hostnames) {
+        $hostnames[] = $host->get('hostname');
+    });
+    return array_unique($hostnames);
+}
diff --git a/Utility/Input.php b/Utility/Input.php
new file mode 100644
index 0000000..8a905b9
--- /dev/null
+++ b/Utility/Input.php
@@ -0,0 +1,192 @@
+ 1 ? $realDomain : $wwwDomain;
+    $suggestions = [$realDomain, $wwwDomain, $previewDomain];
+    return askDomain($question, $defaultDomain, $suggestions);
+}
+
+/**
+ * Ask the user for confirmation, if true ask second question
+ *
+ * @param string $question
+ * @param string|null $questionIfTrue
+ * @param boolean $default
+ * @param boolean $required
+ * @return bool|string
+ */
+function askConfirmationInput(string $question, ?string $questionIfTrue = null, bool $default = false, bool $required = false)
+{
+    $question = parse($question);
+    if (isset($questionIfTrue)) {
+        $questionIfTrue = parse($questionIfTrue);
+    }
+    $q1Length = getLength($question) + 6;
+    $q2Length = getLength($questionIfTrue);
+    $placeholderQ1 = $q1Length < $q2Length ? $q2Length - $q1Length : 0;
+    $placeholderQ2 = $q1Length > $q2Length ? $q1Length - $q2Length : 0;
+
+    $answer = askConfirmation(" $question " . str_repeat(' ', $placeholderQ1), $default);
+    if ($answer === false) {
+        writeCleanLine(" No \n");
+        return false;
+    }
+    if (!isset($questionIfTrue)) {
+        writeCleanLine(" Yes \n");
+        return true;
+    }
+    return askln($questionIfTrue . str_repeat(' ', $placeholderQ2), $required);
+}
+
+/**
+ * Ask the user for input
+ *
+ * @param string $question
+ * @param boolean $required
+ * @param string|null $default
+ * @param boolean $hidden
+ * @return string|null
+ */
+function askln(string $question, bool $required = false, ?string $default = null, bool $hidden = false, string $prefix = ''): ?string
+{
+    if (is_string($default)) {
+        $default = parse($default);
+    }
+
+    if (strlen($prefix)) {
+        $prefix = " $prefix ";
+    }
+
+    if ($required === true) {
+        $answer = null;
+        while ($answer === null) {
+            writeCleanLine(" $question ");
+            $answer = $hidden ? askHiddenResponse($prefix) : _ask($prefix, $default);
+        }
+        writeCleanLine();
+        return $answer;
+    }
+    writeCleanLine(" $question ");
+    $answer = $hidden ? askHiddenResponse($prefix) : _ask($prefix, $default);
+    writeCleanLine();
+    return $answer;
+}
+
+/**
+ * @param string $message
+ * @param string[] $availableChoices
+ * @param string|int|null $default
+ * @param bool|false $multiselect
+ * @return string|string[]
+ * @codeCoverageIgnore
+ */
+function askChoiceln(string $message, array $availableChoices, $default = null, bool $multiselect = false)
+{
+    Context::required(__FUNCTION__);
+    $message = parse($message);
+    if (empty($availableChoices)) {
+        throw new InvalidArgumentException('Available choices should not be empty');
+    }
+
+    if ($default !== null && !array_key_exists($default, $availableChoices)) {
+        throw new InvalidArgumentException('Default choice is not available');
+    }
+
+    if (output()->isQuiet()) {
+        if ($default === null) {
+            $default = key($availableChoices);
+        }
+        return [$default => $availableChoices[$default]];
+    }
+
+    if (Deployer::isWorker()) {
+        return Deployer::proxyCallToMaster(currentHost(), __FUNCTION__, ...func_get_args());
+    }
+
+    /** @var QuestionHelper */
+    $helper = Deployer::get()->getHelper('question');
+    $message = " $message" . (($default === null) ? "" : " ($default)") . " ";
+    $length = getLength($message);
+    $placeholder = '' . str_repeat(' ', $length) . '';
+
+    writeCleanLine($placeholder);
+    writeCleanLine($message);
+    $question = new ChoiceQuestion($placeholder, $availableChoices, $default);
+    $question->setMultiselect($multiselect);
+
+    return $helper->ask(input(), output(), $question);
+}
+
+
+function _ask(string $message, ?string $default = null, ?array $autocomplete = null): ?string
+{
+    // if (defined('DEPLOYER_NO_ASK')) {
+    //     throw new WillAskUser($message);
+    // }
+    // Context::required(__FUNCTION__);
+
+    if (output()->isQuiet()) {
+        return $default;
+    }
+
+    if (Deployer::isWorker()) {
+        return Deployer::proxyCallToMaster(currentHost(), __FUNCTION__, ...func_get_args());
+    }
+
+    /** @var QuestionHelper */
+    $helper = Deployer::get()->getHelper('question');
+
+    $tag = currentHost()->getTag();
+    $message = parse($message);
+    $message = "$message " . (($default === null) ? "" : "(default: $default) ");
+
+    $question = new Question($message, $default);
+    if (!empty($autocomplete)) {
+        $question->setAutocompleterValues($autocomplete);
+    }
+
+    return $helper->ask(input(), output(), $question);
+}
diff --git a/Utility/Output.php b/Utility/Output.php
new file mode 100644
index 0000000..20849d0
--- /dev/null
+++ b/Utility/Output.php
@@ -0,0 +1,126 @@
+ gets printed bold, 
 / 
 create a linebreak
+ * @param string|null The type of the box.
+ * @return void
+ */
+function writebox(string $content, ?string $type = 'bgInfo'): void
+{
+    $background = null;
+    $color = null;
+
+    switch (strtolower($type)) {
+        case 'info':
+            $color = 'blue';
+            break;
+        case 'success':
+            $color = 'green';
+            break;
+        case 'warning':
+            $color = 'yellow';
+            break;
+        case 'error':
+            $color = 'red';
+            break;
+        case 'bginfo':
+            $background = 'blue';
+            $color = 'white';
+            break;
+        case 'bgsuccess':
+            $background = 'green';
+            $color = 'black';
+            break;
+        case 'bgwarning':
+            $background = 'yellow';
+            $color = 'black';
+            break;
+        case 'bgerror':
+            $background = 'red';
+            $color = 'white';
+            break;
+    }
+
+    // Parse content
+    $content = parse($content);
+
+    // Replace strong with bold notation
+    if (isset($background)) {
+        $content = str_replace(
+            ['', ''],
+            ["", '>'],
+            $content
+        );
+    } elseif (isset($color)) {
+        $content = str_replace(
+            ['', ''],
+            ["", '>'],
+            $content
+        );
+    } else {
+        $content = str_replace(
+            ['', ''],
+            ["", '>'],
+            $content
+        );
+    }
+
+    // Replace br tags with a linebreak
+    $contentArray = preg_split('/(
]*>|\n)/i', $content);
+    $contents = [];
+    $maxLength = 0;
+    foreach ($contentArray as $key => $string) {
+        $length = getLength($string);
+        $contents[$key] = compact('length', 'string');
+        if ($length > $maxLength) {
+            $maxLength = $length;
+        }
+    }
+    $placeholder = str_repeat(' ', $maxLength);
+
+    writeCleanLine();
+    writeCleanLine($placeholder, $color, $background);
+    foreach ($contents as $array) {
+        $space = str_repeat(' ', $maxLength - $array['length']);
+        writeCleanLine($array['string'] . $space, $color, $background);
+    }
+    writeCleanLine($placeholder, $color, $background);
+    writeCleanLine();
+}
+
+
+/**
+ * Write line with optional colors
+ *
+ * @param string|null $content
+ * @param string|null $background
+ * @param string|null $color
+ * @return void
+ */
+function writeCleanLine(
+    ?string $content = null,
+    ?string $color = null,
+    ?string $background = null
+): void {
+    if (!$content) {
+        $content = '   ';
+    }
+
+    $content = " $content ";
+
+    if (isset($background)) {
+        $content = "$content>";
+    } elseif (isset($color)) {
+        $content = "$content>";
+    }
+    output()->writeln($content);
+}
diff --git a/Utility/Runner.php b/Utility/Runner.php
new file mode 100644
index 0000000..676408b
--- /dev/null
+++ b/Utility/Runner.php
@@ -0,0 +1,44 @@
+/dev/null 2>&1')) {
+        return trim(runLocally($command));
+    }
+
+    return null;
+}
+
+/**
+ * Run a interactive shell on the server
+ *
+ * @param string $command
+ * @return void
+ */
+function ttyRun(string $command): void
+{
+    $command = parse("VISUAL={{editor}} $command");
+    $host = currentHost();
+    $connectionString = $host->getConnectionString();
+    $ssh = ['ssh', '-A', '-tt', $connectionString, $command];
+    $process = new Process($ssh);
+    $process->setTimeout(null)->setIdleTimeout(null)->setTty(true);
+    $process->run();
+}
diff --git a/Utility/StringHelper.php b/Utility/StringHelper.php
new file mode 100644
index 0000000..ed892a1
--- /dev/null
+++ b/Utility/StringHelper.php
@@ -0,0 +1,48 @@
+ $database) {
-            $array[$database] = $start + $index;
-        }
-    }
-    return $array;
+set('local_pwd', static function (): string {
+    return runLocally('echo $PWD');
 });
 
-// Composer specific
-set('composer_options', '{{composer_action}} --verbose --prefer-dist --no-progress --no-interaction --no-dev --optimize-autoloader');
+set('local/gh', static function (): ?string {
+    return whichLocally('gh');
+});
 
-// Git specifc values
 set('bin/git', static function (): string {
-    return 'GIT_SSH_COMMAND="ssh -i ~/.ssh/' . get('ssh_key') . '" ' . locateBinaryPath('git');
+    return 'GIT_SSH_COMMAND="ssh -i ~/.ssh/' . get('ssh_key') . '" ' . which('git');
 });
 
 set('repository_url_parts', static function (): array {
@@ -88,54 +49,63 @@
     return 'https://' . get('repository_url_parts')['host'] . '/' . get('repository_url_parts')['path'];
 });
 
-set('deploy_user', static function (): string {
-    $user = getenv('GIT_AUTHOR_NAME');
-    if ($user === false) {
-        $user = getenv('GIT_COMMITTER_NAME');
-        if ($user === false) {
-            $getUserCommand = 'git config --get user.name';
-            if (!testLocally("$getUserCommand 2>/dev/null || true")) {
-                $user = runLocally($getUserCommand);
-            } else {
-                $user = get('user');
-            }
-        }
+set('stage', static function (): ?string {
+    $labels = get('labels');
+    if (isset($labels) && $labels['stage']) {
+        return $labels['stage'];
     }
-    return $user;
+    return null;
 });
 
-set('git_commit_types', [
-    'Fix'      => 'A bug fix',
-    'Update'   => 'A backwards-compatible enhancement',
-    'Breaking' => 'A backwards-incompatible enhancement',
-    'Docs'     => 'Documentation change',
-    'Build'    => 'Build process update',
-    'New'      => 'A new feature implementation',
-    'Upgrade'  => 'Dependency upgrade',
-    'Chore'    => 'Other changes (e.g.: refactoring)',
-]);
-
-// Connection specifc values
-set('port', 22);
-set('forwardAgent', false);
-set('multiplexing', true);
-set('ssh_key', get('repository_short_name'));
-
-// Server specifc values
-set('editor', 'nano');
-set('html_path', '/var/www/virtual/{{user}}');
-set('deploy_path', '{{html_path}}/{{deploy_folder}}');
-set('db_backup_folder', '{{deploy_path}}/.dep/databases/dumps');
-set('db_backup_keep_dumps', 5);
-
 set('deploy_folder', static function (): string {
     $suffix = '';
-    if (has('stage')) {
-        $suffix = "/" . ucfirst(get('stage'));
+    $stage = get('stage');
+    if (isset($stage)) {
+        $suffix = "/" . ucfirst($stage);
     }
     return get('repository_short_name') . $suffix;
 });
 
+set('deploy_user', static function (): string {
+    $user = getenv('GIT_AUTHOR_NAME') || getenv('GIT_COMMITTER_NAME');
+    if ($user !== false) {
+        return $user;
+    }
+
+    $getUserCommand = 'git config --get user.name';
+    if (!testLocally("$getUserCommand 2>/dev/null || true")) {
+        $user = runLocally($getUserCommand);
+        if ($user !== 'DDEV-Local User') {
+            return $user;
+        }
+    }
+    return get('remote_user');
+});
+
+set('db_name', static function (): string {
+    try {
+        $yaml = run('{{flow_command}} configuration:show --type Settings --path Neos.Flow.persistence.backendOptions');
+        return Yaml::parse($yaml)['dbname'];
+    } catch (RuntimeException $e) {
+        $stage = get('stage');
+        $name = has('database') ? '{{database}}' : camelCaseToSnakeCase(get('repository_short_name')) . '_neos';
+        $suffix = !has('database') && $stage ? '_' . camelCaseToSnakeCase($stage) : '';
+        return parse('{{remote_user}}_' . $name . $suffix);
+    }
+});
+
+set('redis_databases_with_numbers', static function (): array {
+    $start = get('redis_start_db_number', 2);
+    $databases = get('redis_databases', false);
+    $array = [];
+    if (is_array($databases)) {
+        foreach ($databases as $index => $database) {
+            $array[$database] = $start + $index;
+        }
+    }
+    return $array;
+});
+
 set('db_password', static function (): string {
     return run('my_print_defaults client | grep -Po "password=\K(\S)*"');
 });
@@ -144,15 +114,6 @@
     return run('date +"%Y-%m-%d__%H-%M-%S"');
 });
 
-// Slack specifc values
 set('slack_application', static function (): string {
-    return get('application', getRealHostname());
+    return get('application', '{{alias}}');
 });
-set('slack_title', '{{slack_application}} (Neos)');
-set('slack_text', '_{{deploy_user}}_ deploying *{{repository_short_name}}* on `{{branch}}` to *{{target}}*');
-set('slack_success_text', 'Deploy from *{{repository_short_name}}* to *{{target}}* successful');
-set('slack_failure_text', 'Deploy from *{{repository_short_name}}* to *{{target}}* failed');
-
-set('slack_color', '#4d91f7');
-set('slack_success_color', '#00c100');
-set('slack_failure_color', '#ff0909');
diff --git a/config.yaml b/config.yaml
new file mode 100644
index 0000000..52596ce
--- /dev/null
+++ b/config.yaml
@@ -0,0 +1,66 @@
+import:
+  - recipe/common.php
+  - Build/Uberspace.Deployer/config.php
+
+config:
+  # Server specifc values
+  editor: 'vim'
+  html_path: '/var/www/virtual/{{remote_user}}'
+  deploy_path: '{{html_path}}/{{deploy_folder}}'
+  db_backup_folder: '{{deploy_path}}/.dep/databases/dumps'
+  db_backup_keep_dumps: 5
+  http_user: '{{remote_user}}'
+  http_group: '{{remote_user}}'
+
+  # Connection specifc values
+  port: 22
+  forwardAgent: false
+  multiplexing: true
+  ssh_key: '{{repository_short_name}}'
+
+  # Flow specifc values
+  flow_context: Production/Live
+  flow_command: 'FLOW_CONTEXT={{flow_context}} {{bin/php}} -d memory_limit=8G {{release_or_current_path}}/flow'
+  shared_dirs:
+    - 'Data/Persistent'
+    - 'Data/Logs'
+    - 'Configuration'
+
+  # To disable the upload you can set this to false: upload_assets_folder: false;
+  # Mostly used for rendered CSS & JS files, who you don't want in your repository
+  upload_assets_folder:
+    - 'DistributionPackages/*/Resources/Private/Templates/InlineAssets'
+    - 'DistributionPackages/*/Resources/Public/Scripts'
+    - 'DistributionPackages/*/Resources/Public/Styles'
+
+  # Redis specfic values
+  redis_start_db_number: 2
+  redis_defaultLifetime: 0
+  redis_databases:
+    - Flow_Mvc_Routing_Route
+    - Flow_Mvc_Routing_Resolve
+    - Neos_Fusion_Content
+    - Flow_Session_MetaData
+    - Flow_Session_Storage
+    - Neos_Media_ImageSize
+    - Flow_Security_Cryptography_HashService
+
+  # git specfic values
+  git_commit_types:
+    Fix: 'A bug fix'
+    Update: 'A backwards-compatible enhancement'
+    Breaking: 'A backwards-incompatible enhancement'
+    Docs: 'Documentation change'
+    Build: 'Build process update'
+    New: 'A new feature implementation'
+    Upgrade: 'Dependency upgrade'
+    Chore: 'Other changes (e.g.: refactoring)'
+
+  # Slack notification
+  slack_title: '{{slack_application}} (Neos)'
+  slack_text: '_{{deploy_user}}_ deploying *{{repository_short_name}}* on `{{branch}}` to *{{target}}*'
+  slack_success_text: 'Deploy from *{{repository_short_name}}* to *{{target}}* successful'
+  slack_failure_text: 'Deploy from *{{repository_short_name}}* to *{{target}}* failed'
+  slack_color: '#4d91f7'
+  slack_success_color: '#00c100'
+  slack_failure_color: '#ff0909'
diff --git a/template/Neos/Settings.yaml b/template/Neos/Settings.yaml
index 5db40ab..9e2e542 100644
--- a/template/Neos/Settings.yaml
+++ b/template/Neos/Settings.yaml
@@ -10,6 +10,6 @@ Neos:
       backendOptions:
         driver: pdo_mysql
         dbname: '{{db_name}}'
-        user: '{{user}}'
+        user: '{{remote_user}}'
         password: '{{db_password}}'
         host: localhost
diff --git a/template/Redis/conf b/template/Redis/conf
index 370a583..8b8b27a 100644
--- a/template/Redis/conf
+++ b/template/Redis/conf
@@ -1,4 +1,4 @@
-unixsocket /home/{{user}}/.redis/sock
+unixsocket /home/{{remote_user}}/.redis/sock
 daemonize no
 port 0
 save ""
\ No newline at end of file