diff --git a/src/freedombone-addremove b/src/freedombone-addremove
index 19aefd18ee62f5206e06573860b90508e394279d..33c709f4709efe768811c3ada244e7e9ec15d4dc 100755
--- a/src/freedombone-addremove
+++ b/src/freedombone-addremove
@@ -57,6 +57,8 @@ done
 
 # End including files
 
+install_is_interactive=1
+
 function mark_unselected_apps_as_removed {
     # Initially mark the apps not chosen on first install as being removed
     # otherwise they may be automatically installed on the next update
@@ -100,6 +102,23 @@ function app_expected_to_be_installed {
     echo "1"
 }
 
+# Select a particular app to be added or removed
+function select_specific_app {
+    app_name="$1"
+    app_is_chosen="$1"
+
+    app_index=0
+    # shellcheck disable=SC2068
+    for a in ${APPS_AVAILABLE[@]}
+    do
+        if [[ "$a" == "$app_name" ]]; then
+            APPS_CHOSEN[$app_index]="$app_is_chosen"
+            break
+        fi
+        app_index=$((app_index+1))
+    done
+}
+
 function show_apps {
     select_all_apps="$1"
     applist=""
@@ -165,24 +184,26 @@ function remove_apps_selected {
         return
     fi
 
-    if [ -f /tmp/.upgrading ]; then
-        dialog --title $"Cannot remove apps" \
-               --msgbox $"A system upgrade is happening, so apps cannot be removed at this time" 6 60
-        return
-    fi
+    if [ $install_is_interactive ]; then
+        if [ -f /tmp/.upgrading ]; then
+            dialog --title $"Cannot remove apps" \
+                   --msgbox $"A system upgrade is happening, so apps cannot be removed at this time" 6 60
+            return
+        fi
 
-    # ask for confirmation
-    dialog --title $"Remove applications" \
-           --backtitle $"Freedombone" \
-           --defaultno \
-           --yesno $"\\nYou have chosen to remove $n apps.\\n\\n    $removals\\n\\nIf you choose 'yes' then this will remove both the applications and their data/messages. If you don't have a backup then you will not be able to recover the data for these applications.\\n\\nAre you sure that you wish to continue?" 15 60
-    sel=$?
-    case $sel in
-        1) return;;
-        255) return;;
-    esac
+        # ask for confirmation
+        dialog --title $"Remove applications" \
+               --backtitle $"Freedombone" \
+               --defaultno \
+               --yesno $"\\nYou have chosen to remove $n apps.\\n\\n    $removals\\n\\nIf you choose 'yes' then this will remove both the applications and their data/messages. If you don't have a backup then you will not be able to recover the data for these applications.\\n\\nAre you sure that you wish to continue?" 15 60
+        sel=$?
+        case $sel in
+            1) return;;
+            255) return;;
+        esac
 
-    clear
+        clear
+    fi
 
     # remove the apps
     read_configuration
@@ -217,38 +238,45 @@ function install_apps_selected {
         return
     fi
 
-    if [[ "$select_all_apps" != "add-all" ]]; then
-        if [ -f /tmp/.upgrading ]; then
-            dialog --title $"Cannot install apps" \
-                   --msgbox $"A system upgrade is happening, so apps cannot be installed at this time" 6 60
-            return
-        fi
+    if [ $install_is_interactive ]; then
+        if [[ "$select_all_apps" != "add-all" ]]; then
+            if [ -f /tmp/.upgrading ]; then
+                dialog --title $"Cannot install apps" \
+                       --msgbox $"A system upgrade is happening, so apps cannot be installed at this time" 6 60
+                return
+            fi
 
-        # ask for confirmation
-        if [ $n -eq 1 ]; then
-            dialog --title $"$installs" \
-                   --backtitle $"Freedombone" \
-                   --defaultno \
-                   --yesno $"\\nThis will install the $installs app\\n\\nProceed?" 9 40
-        else
-            dialog_height=$((15 + "$n"))
-            dialog --title $"Add applications" \
-                   --backtitle $"Freedombone" \
-                   --defaultno \
-                   --yesno $"\\nYou have chosen to install $n apps\\n\\n    $installs\\n\\nProceed?" $dialog_height 60
+            # ask for confirmation
+            if [ $n -eq 1 ]; then
+                dialog --title $"$installs" \
+                       --backtitle $"Freedombone" \
+                       --defaultno \
+                       --yesno $"\\nThis will install the $installs app\\n\\nProceed?" 9 40
+            else
+                dialog_height=$((15 + "$n"))
+                dialog --title $"Add applications" \
+                       --backtitle $"Freedombone" \
+                       --defaultno \
+                       --yesno $"\\nYou have chosen to install $n apps\\n\\n    $installs\\n\\nProceed?" $dialog_height 60
+            fi
+            sel=$?
+            case $sel in
+                1) return;;
+                255) return;;
+            esac
         fi
-        sel=$?
-        case $sel in
-            1) return;;
-            255) return;;
-        esac
-    fi
 
-    clear
+        clear
+
+        # install the apps
+        read_configuration
+        install_apps interactive
+    else
+        # install the apps
+        read_configuration
+        install_apps
+    fi
 
-    # install the apps
-    read_configuration
-    install_apps interactive
     if [ ! "$APP_INSTALLED_SUCCESS" ]; then
         echo $'One or more apps failed to install'
     fi
@@ -267,15 +295,33 @@ if [[ ${#APPS_AVAILABLE[@]} == 0 ]]; then
     exit 1
 fi
 
-show_apps "$1"
-mark_unselected_apps_as_removed "$1"
+args="$1"
 
-clear
+if [ "$2" ]; then
+    if [[ "$args" == "add" || "$args" == "remove" ]]; then
+        if [[ "$args" == "add" ]]; then
+            select_specific_app "$2" "1"
+        else
+            select_specific_app "$2" "0"
+        fi
+        install_is_interactive=
+    else
+        show_apps "$args"
+    fi
+else
+    show_apps "$args"
+fi
+
+mark_unselected_apps_as_removed "$args"
+
+if [ $install_is_interactive ]; then
+    clear
+fi
 
 remove_apps_selected
 
-if [[ "$1" == "add-all" ]]; then
-    install_apps_selected add-all
+if [[ "$args" == "add-all" ]]; then
+    install_apps_selected "$args"
 else
     install_apps_selected
 fi
diff --git a/src/freedombone-installer b/src/freedombone-installer
new file mode 100755
index 0000000000000000000000000000000000000000..2a7783cf9484f77293cbddd40743959a9dd7646c
--- /dev/null
+++ b/src/freedombone-installer
@@ -0,0 +1,129 @@
+#!/bin/bash
+#  _____               _           _
+# |   __|___ ___ ___ _| |___ _____| |_ ___ ___ ___
+# |   __|  _| -_| -_| . | . |     | . | . |   | -_|
+# |__|  |_| |___|___|___|___|_|_|_|___|___|_|_|___|
+#
+#                              Freedom in the Cloud
+#
+# Install daemon for the web admin system
+#
+# License
+# =======
+#
+# Copyright (C) 2018 Bob Mottram <bob@freedombone.net>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+PROJECT_NAME='freedombone'
+
+export TEXTDOMAIN=${PROJECT_NAME}-installer
+export TEXTDOMAINDIR="/usr/share/locale"
+
+#COMPLETION_FILE="/root/${PROJECT_NAME}-completed.txt"
+CONFIGURATION_FILE="/root/${PROJECT_NAME}.cfg"
+
+# Start including files
+
+#source "$PROJECT_INSTALL_DIR/${PROJECT_NAME}-vars"
+
+#UTILS_FILES="/usr/share/${PROJECT_NAME}/utils/${PROJECT_NAME}-utils-*"
+#for f in $UTILS_FILES
+#do
+#    source "$f"
+#done
+
+#APP_FILES="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*"
+#for f in $APP_FILES
+#do
+#    source "$f"
+#done
+
+# End including files
+
+local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local
+
+pending_removes="/var/www/${local_hostname}/htdocs/admin/pending_removes.txt"
+pending_installs="/var/www/${local_hostname}/htdocs/admin/pending_installs.txt"
+
+while true
+do
+    if [ -f "$pending_installs" ]; then
+        linestr=$(head -n 1 "$pending_installs")
+        if [[ "$linestr" == "install_"* ]]; then
+            app_name=$(echo "$linestr" | awk -F '_' '{print $2}' | awk -F ' ' '{print $1}')
+            if [ -f "/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${app_name}" ]; then
+                app_domain=$(echo "$linestr" | awk -F ' ' '{print $2}')
+                app_name_upper=$(echo "$app_name" | awk '{print toupper($0)}')
+                freedns_code=$(echo "$linestr" | awk -F ' ' '{print $3}')
+
+                # indicate that we are installing
+                if [[ "$linestr" != *'_running'* ]]; then
+                    sed -i "s|${app_name}|${app_name}_running|g" "$pending_installs"
+                fi
+
+                # Add domain name to the config
+                if ! grep -q "${app_name_upper}_DOMAIN_NAME=" $CONFIGURATION_FILE; then
+                    echo "${app_name_upper}_DOMAIN_NAME=${app_domain}" >> $CONFIGURATION_FILE
+                else
+                    sed -i "s|${app_name_upper}_DOMAIN_NAME=.*|${app_name_upper}_DOMAIN_NAME=${app_domain}|g" $CONFIGURATION_FILE
+                fi
+
+                # Add freedns code to the config
+                if [ "$freedns_code" ]; then
+                    # shellcheck disable=SC2086
+                    ${app_name_upper}_CODE="${freedns_code}"
+
+                    if ! grep -q "${app_name_upper}_CODE=" $CONFIGURATION_FILE; then
+                        echo "${app_name_upper}_CODE=${freedns_code}" >> $CONFIGURATION_FILE
+                    else
+                        sed -i "s|${app_name_upper}_CODE=.*|${app_name_upper}_CODE=${freedns_code}|g" $CONFIGURATION_FILE
+                    fi
+                fi
+
+                /usr/local/bin/${PROJECT_NAME}-addremove add "${app_name}"
+            fi
+            # remove the line
+            sed -i "/$linestr/d" "$pending_installs"
+        else
+            # if any unusual line is found then remove the file
+            rm "$pending_installs"
+        fi
+    fi
+
+    sleep 1
+
+    if [ -f "$pending_removes" ]; then
+        linestr=$(head -n 1 "$pending_removes")
+        if [[ "$linestr" == "remove_"* ]]; then
+            app_name=$(echo "$linestr" | awk -F '_' '{print $2}')
+            if [ -f "/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${app_name}" ]; then
+                # indicate that we are removing
+                if [[ "$linestr" != *'_running'* ]]; then
+                    sed -i "s|${app_name}|${app_name}_running|g" "$pending_removes"
+                fi
+                /usr/local/bin/${PROJECT_NAME}-addremove remove "${app_name}"
+            fi
+            # remove the line
+            sed -i "/$linestr/d" "$pending_removes"
+        else
+            # if any unusual line is found then remove the file
+            rm "$pending_removes"
+        fi
+    fi
+
+    sleep 1
+done
+
+exit 0
diff --git a/src/freedombone-utils-webadmin b/src/freedombone-utils-webadmin
index 10c6b9c32f8e23af8ffa9dd65abe778f608fd9fb..6b182c70f0ac9900d8899a9088ca88c0c7cfeb36 100755
--- a/src/freedombone-utils-webadmin
+++ b/src/freedombone-utils-webadmin
@@ -27,6 +27,30 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+function web_admin_configure_installer_daemon {
+    if [ -f /etc/systemd/system/webadmin.service ]; then
+        return
+    fi
+
+    { echo '[Unit]';
+      echo 'Description=Installer daemon for web admin';
+      echo 'After=network.target';
+      echo '';
+      echo '[Service]';
+      echo 'User=root';
+      echo "ExecStart=/usr/local/bin/${PROJECT_NAME}-installer";
+      echo "ExecReload=/bin/kill \$MAINPID";
+      echo 'KillMode=process';
+      echo 'Restart=always';
+      echo '';
+      echo '[Install]';
+      echo 'WantedBy=multi-user.target';
+      echo 'Alias=pleroma.service'; } > /etc/systemd/system/webadmin.service
+    systemctl enable webadmin
+    systemctl daemon-reload
+    systemctl start webadmin
+}
+
 function web_admin_avahi {
     if [ -f /etc/avahi/services/webadmin.service ]; then
         return
@@ -389,6 +413,7 @@ function web_admin_create_installed_apps {
 function install_web_admin {
     # This is intended as an admin web user interface
     # similar to Plinth or the yunohost
+
     local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local
 
     if [ ! -d "/var/www/${local_hostname}/htdocs/admin" ]; then
@@ -510,6 +535,8 @@ function install_web_admin {
     chown -R www-data:www-data "/var/www/${local_hostname}/htdocs"
 
     web_admin_avahi
+
+    web_admin_configure_installer_daemon
 }
 
 # NOTE: deliberately no exit 0
diff --git a/webadmin/installapp.php b/webadmin/installapp.php
index 0417253db48389a9042a6bfc5a9bb50a06dae3a2..33ecc01e07ec549b03f4dceae320014b39acf36e 100755
--- a/webadmin/installapp.php
+++ b/webadmin/installapp.php
@@ -5,6 +5,7 @@ $output_filename = "apps_add.html";
 if (isset($_POST['install'])) {
     $app_name = htmlspecialchars($_POST['app_name']);
     $install_domain = $_POST['install_domain'];
+    $freedns_code = $_POST['freedns_code'];
 
     $continue_install=true;
     if(file_exists("pending_removes.txt")) {
@@ -29,7 +30,7 @@ if (isset($_POST['install'])) {
 
         if(! exec('grep '.escapeshellarg("install_".$app_name).' ./pending_installs.txt')) {
             $pending_installs = fopen("pending_installs.txt", "a") or die("Unable to append to installs file");
-            fwrite($pending_installs, "install_".$app_name."\n");
+            fwrite($pending_installs, "install_".$app_name." ".$install_domain." ".$freedns_code."\n");
             fclose($pending_installs);
             $output_filename = "app_installing.html";
         }