diff --git a/package.json b/package.json
index 73c2fc16d15b12b7a431c03d270a226d7ce882d6..8d5cd449e3996e4e029c2931b41bf9dab73fe6f7 100644
--- a/package.json
+++ b/package.json
@@ -65,7 +65,6 @@
     "vue-i18n": "^8.9.0",
     "vue-router": "3.0.2",
     "vue-splitpane": "1.0.2",
-    "vue2-ace-editor": "^0.0.13",
     "vuedraggable": "^2.16.0",
     "vuex": "3.0.1",
     "xlsx": "^0.11.16"
diff --git a/src/api/settings.js b/src/api/settings.js
index c1bbb182be3401e7d6df253e3fe69cca90ccb125..9d1c28902c1d213834a7557767958845ffeddb4d 100644
--- a/src/api/settings.js
+++ b/src/api/settings.js
@@ -40,16 +40,4 @@ export async function removeSettings(configs, authHost, token) {
   })
 }
 
-export async function uploadMedia(file, authHost, token) {
-  const formData = new FormData()
-  formData.append('file', file)
-  return await request({
-    baseURL: baseName(authHost),
-    url: `/api/v1/media`,
-    method: 'post',
-    headers: authHeaders(token),
-    data: formData
-  })
-}
-
 const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}
diff --git a/src/lang/en.js b/src/lang/en.js
index 3dcd9bddd672efafaf27ebdbb9ad24ba53b22c30..1a8a8c9007c725227d3c000078b1427012caaec1 100644
--- a/src/lang/en.js
+++ b/src/lang/en.js
@@ -339,12 +339,10 @@ export default {
     mediaProxy: 'Media Proxy',
     metadata: 'Metadata',
     gopher: 'Gopher',
-    endpoint: 'Endpoint',
     jobQueue: 'Job queue',
     webPush: 'Web push encryption',
     esshd: 'BBS / SSH access',
     rateLimiters: 'Rate limiters',
-    database: 'Database',
     other: 'Other',
     relays: 'Relays',
     follow: 'Follow',
@@ -383,6 +381,7 @@ export default {
     file: 'File',
     update: 'Update',
     remove: 'Remove',
+    removeFromDB: 'Remove setting from the DB',
     selectLocalPack: 'Select the local pack to copy to',
     localPack: 'Local pack',
     specifyShortcode: 'Specify a custom shortcode',
@@ -405,7 +404,8 @@ export default {
     nowNewPacksToImport: 'No new packs to import',
     successfullyUpdated: 'Successfully updated',
     metadatLowerCase: 'metadata',
-    files: 'files'
+    files: 'files',
+    successfullyRemoved: 'Setting removed successfully!'
   },
   invites: {
     inviteTokens: 'Invite tokens',
diff --git a/src/store/modules/normalizers.js b/src/store/modules/normalizers.js
index 17b7c5f0f1b32a74a5654ca6305094e1e3a1aff3..6d0efe29f6c3a145d1088fb384a016a23b7fc64d 100644
--- a/src/store/modules/normalizers.js
+++ b/src/store/modules/normalizers.js
@@ -1,3 +1,5 @@
+import _ from 'lodash'
+
 export const checkPartialUpdate = (settings, updatedSettings, description) => {
   return Object.keys(updatedSettings).reduce((acc, group) => {
     acc[group] = Object.keys(updatedSettings[group]).reduce((acc, key) => {
@@ -20,12 +22,22 @@ export const checkPartialUpdate = (settings, updatedSettings, description) => {
   }, {})
 }
 
-const getCurrentValue = (object, keys) => {
-  if (keys.length === 0) {
-    return object
+const getCurrentValue = (type, value, path) => {
+  if (type === 'state') {
+    return _.get(value, path)
+  } else {
+    const [firstSettingName, ...restKeys] = path
+    const firstSegment = value[firstSettingName]
+    if (restKeys.length === 0 || !firstSegment) {
+      return firstSegment || false
+    } else {
+      const secondSegment = (value, keys) => {
+        const [element, ...rest] = keys
+        return keys.length === 0 ? value : secondSegment(value[1][element], rest)
+      }
+      return secondSegment(firstSegment, restKeys)
+    }
   }
-  const [currentKey, ...restKeys] = keys
-  return getCurrentValue(object[currentKey], restKeys)
 }
 
 const getValueWithoutKey = (key, [type, value]) => {
@@ -136,24 +148,26 @@ export const processNested = (valueForState, valueForUpdatedSettings, group, par
   const [{ key, type }, ...otherParents] = parents
   const path = [group, parentKey, ...parents.reverse().map(parent => parent.key).slice(0, -1)]
 
-  let updatedValueForState = valueExists(settings, path)
-    ? { ...getCurrentValue(settings[group][parentKey], parents.map(el => el.key).slice(0, -1)),
+  let updatedValueForState = valueExists('state', settings, path)
+    ? { ...getCurrentValue('state', settings[group][parentKey], parents.map(el => el.key).slice(0, -1)),
       ...{ [key]: valueForState }}
     : { [key]: valueForState }
-  let updatedValueForUpdatedSettings = valueExists(updatedSettings, path)
-    ? { ...getCurrentValue(updatedSettings[group][parentKey], parents.map(el => el.key).slice(0, -1))[1],
+  let updatedValueForUpdatedSettings = valueExists('updatedSettings', updatedSettings, path)
+    ? { ...getCurrentValue('updatedSettings', updatedSettings[group][parentKey], parents.map(el => el.key).slice(0, -1))[1],
       ...{ [key]: [type, valueForUpdatedSettings] }}
     : { [key]: [type, valueForUpdatedSettings] }
 
   if (group === ':mime' && parents[0].key === ':types') {
-    updatedValueForState = { ...settings[group][parents[0].key].value, ...updatedValueForState }
-    updatedValueForUpdatedSettings = {
-      ...Object.keys(settings[group][parents[0].key].value)
+    updatedValueForState = settings[group][parents[0].key]
+      ? { ...settings[group][parents[0].key].value, ...updatedValueForState }
+      : updatedValueForState
+    updatedValueForUpdatedSettings = settings[group][parents[0].key]
+      ? { ...Object.keys(settings[group][parents[0].key].value)
         .reduce((acc, el) => {
           return { ...acc, [el]: [type, settings[group][parents[0].key].value[el]] }
         }, {}),
-      ...updatedValueForUpdatedSettings
-    }
+      ...updatedValueForUpdatedSettings }
+      : updatedValueForUpdatedSettings
   }
 
   return otherParents.length === 1
@@ -161,12 +175,25 @@ export const processNested = (valueForState, valueForUpdatedSettings, group, par
     : processNested(updatedValueForState, updatedValueForUpdatedSettings, group, parentKey, otherParents, settings, updatedSettings)
 }
 
-const valueExists = (value, path) => {
-  if (path.length === 0) {
-    return true
+const valueExists = (type, value, path) => {
+  if (type === 'state') {
+    return _.get(value, path)
+  } else {
+    const [group, key, firstSettingName, ...restKeys] = path
+    const firstSegment = _.get(value, [group, key, firstSettingName])
+    if (restKeys.length === 0 || !firstSegment) {
+      return firstSegment || false
+    } else {
+      const secondSegment = (value, keys) => {
+        if (keys.length === 0) {
+          return true
+        }
+        const [element, ...rest] = keys
+        return value[1][element] ? secondSegment(value[1][element], rest) : false
+      }
+      return secondSegment(firstSegment, restKeys)
+    }
   }
-  const [element, ...rest] = path
-  return value[element] ? valueExists(value[element], rest) : false
 }
 
 export const valueHasTuples = (key, value) => {
@@ -218,8 +245,6 @@ const wrapValues = (settings, currentState) => {
     } else if (setting === ':ip') {
       const ip = value.split('.').map(s => parseInt(s, 10))
       return { 'tuple': [setting, { 'tuple': ip }] }
-    } else if (setting === ':ssl_options') {
-      return { 'tuple': [setting, wrapValues(value, currentState)] }
     } else if (setting === ':args') {
       const index = value.findIndex(el => el === 'implode')
       const updatedArray = value.slice()
diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js
index 7efc29b51b213baab3fac5a969975c3b225ba6c2..c102c6eb94dbb87acd8661dfd0e38f8aa06e776f 100644
--- a/src/store/modules/settings.js
+++ b/src/store/modules/settings.js
@@ -1,32 +1,25 @@
-import { fetchDescription, fetchSettings, removeSettings, updateSettings, uploadMedia } from '@/api/settings'
+import { fetchDescription, fetchSettings, removeSettings, updateSettings } from '@/api/settings'
 import { checkPartialUpdate, parseNonTuples, parseTuples, valueHasTuples, wrapUpdatedSettings } from './normalizers'
+import _ from 'lodash'
 
 const settings = {
   state: {
     description: [],
-    settings: {
-      ':auto_linker': {},
-      ':cors_plug': {},
-      ':esshd': {},
-      ':http_signatures': {},
-      ':logger': {},
-      ':mime': {},
-      ':phoenix': {},
-      ':pleroma': {},
-      ':prometheus': {},
-      ':quack': {},
-      ':tesla': {},
-      ':ueberauth': {},
-      ':web_push_encryption': {}
-    },
+    settings: {},
     updatedSettings: {},
-    ignoredIfNotEnabled: ['enabled', 'handler', 'password_authenticator', 'port', 'priv_dir'],
+    db: {},
     loading: true
   },
   mutations: {
     CLEAR_UPDATED_SETTINGS: (state) => {
       state.updatedSettings = {}
     },
+    REMOVE_SETTING_FROM_UPDATED: (state, { group, key, subkeys }) => {
+      if (_.get(state.updatedSettings, [group, key, subkeys[0]])) {
+        const { [subkeys[0]]: value, ...updatedSettings } = state.updatedSettings[group][key]
+        state.updatedSettings = updatedSettings
+      }
+    },
     SET_DESCRIPTION: (state, data) => {
       state.description = data
     },
@@ -38,10 +31,19 @@ const settings = {
         const parsedValue = valueHasTuples(key, value)
           ? { value: parseNonTuples(key, value) }
           : parseTuples(value, key)
-        acc[group][key] = { ...acc[group][key], ...parsedValue }
+        acc[group] = acc[group] ? { ...acc[group], [key]: parsedValue } : { [key]: parsedValue }
+        return acc
+      }, {})
+
+      const newDbSettings = data.reduce((acc, { group, key, db }) => {
+        if (db) {
+          acc[group] = acc[group] ? { ...acc[group], [key]: db } : { [key]: db }
+        }
         return acc
-      }, state.settings)
+      }, {})
+
       state.settings = newSettings
+      state.db = newDbSettings
     },
     UPDATE_SETTINGS: (state, { group, key, input, value, type }) => {
       const updatedSetting = !state.updatedSettings[group] || (key === 'Pleroma.Emails.Mailer' && input === ':adapter')
@@ -66,8 +68,12 @@ const settings = {
       commit('SET_SETTINGS', response.data.configs)
       commit('SET_LOADING', false)
     },
-    async RemoveSetting({ getters }, configs) {
+    async RemoveSetting({ commit, getters }, configs) {
       await removeSettings(configs, getters.authHost, getters.token)
+      const response = await fetchSettings(getters.authHost, getters.token)
+      const { group, key, subkeys } = configs[0]
+      commit('SET_SETTINGS', response.data.configs)
+      commit('REMOVE_SETTING_FROM_UPDATED', { group, key, subkeys: subkeys || [] })
     },
     async SubmitChanges({ getters, commit, state }) {
       const updatedData = checkPartialUpdate(state.settings, state.updatedSettings, state.description)
@@ -75,7 +81,8 @@ const settings = {
         return [...acc, ...wrapUpdatedSettings(group, updatedData[group], state.settings)]
       }, [])
 
-      const response = await updateSettings(configs, getters.authHost, getters.token)
+      await updateSettings(configs, getters.authHost, getters.token)
+      const response = await fetchSettings(getters.authHost, getters.token)
       commit('SET_SETTINGS', response.data.configs)
       commit('CLEAR_UPDATED_SETTINGS')
     },
@@ -84,24 +91,14 @@ const settings = {
         ? commit('UPDATE_SETTINGS', { group, key, input, value, type })
         : commit('UPDATE_SETTINGS', { group, key: input, input: '_value', value, type })
     },
-    UpdateState({ commit, dispatch, state }, { group, key, input, value }) {
+    async UpdateState({ commit, getters, state }, { group, key, input, value }) {
       if (key === 'Pleroma.Emails.Mailer' && input === ':adapter') {
         const subkeys = Object.keys(state.settings[group][key]).filter(el => el !== ':adapter')
-        const emailsValue = subkeys.map(el => {
-          return { 'tuple': [el, state.settings[group][key][el]] }
-        })
-        dispatch('RemoveSetting', [{ group, key, value: emailsValue, delete: true, subkeys }])
+        await removeSettings([{ group, key, delete: true, subkeys }], getters.authHost, getters.token)
       }
       key
         ? commit('UPDATE_STATE', { group, key, input, value })
         : commit('UPDATE_STATE', { group, key: input, input: 'value', value })
-    },
-    async UploadMedia({ dispatch, getters, state }, { file, tab, inputName, childName }) {
-      const response = await uploadMedia(file, getters.authHost, getters.token)
-      const updatedValue = childName
-        ? { ...state.settings[tab][inputName], ...{ [childName]: response.data.url }}
-        : response.data.url
-      dispatch('UpdateSettings', { tab, data: { [inputName]: updatedValue }})
     }
   }
 }
diff --git a/src/views/settings/components/ActivityPub.vue b/src/views/settings/components/ActivityPub.vue
index 4c99296d0864d8420354b4a51b60e74338a8ae22..2eb9a654170e36c05ab3c248f4334299b994bf38 100644
--- a/src/views/settings/components/ActivityPub.vue
+++ b/src/views/settings/components/ActivityPub.vue
@@ -17,6 +17,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'ActivityPub',
@@ -29,13 +30,13 @@ export default {
       return this.settings.description.find(setting => setting.key === ':activitypub')
     },
     activitypubData() {
-      return this.settings.settings[':pleroma'][':activitypub']
+      return _.get(this.settings.settings, [':pleroma', ':activitypub']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.$store.state.settings.loading
@@ -44,7 +45,7 @@ export default {
       return this.settings.description.find(setting => setting.key === ':user')
     },
     userData() {
-      return this.settings.settings[':pleroma'][':user']
+      return _.get(this.settings.settings, [':pleroma', ':user']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/Authentication.vue b/src/views/settings/components/Authentication.vue
index c37fe5376b0f4586352fe5a4bd61ed259c0bb9bc..f88b5b40f4d76fe0f3534dbd5da4c914592eb924 100644
--- a/src/views/settings/components/Authentication.vue
+++ b/src/views/settings/components/Authentication.vue
@@ -25,6 +25,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Authentication',
@@ -37,19 +38,19 @@ export default {
       return this.settings.description.find(setting => setting.key === ':auth')
     },
     authData() {
-      return this.settings.settings[':pleroma'][':auth']
+      return _.get(this.settings.settings, [':pleroma', ':auth']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     ldap() {
       return this.settings.description.find(setting => setting.key === ':ldap')
     },
     ldapData() {
-      return this.settings.settings[':pleroma'][':ldap']
+      return _.get(this.settings.settings, [':pleroma', ':ldap']) || {}
     },
     loading() {
       return this.settings.loading
@@ -58,13 +59,13 @@ export default {
       return this.settings.description.find(setting => setting.key === ':oauth2')
     },
     oauth2Data() {
-      return this.settings.settings[':pleroma'][':oauth2']
+      return _.get(this.settings.settings, [':pleroma', ':oauth2']) || {}
     },
     pleromaAuthenticator() {
       return this.settings.description.find(setting => setting.description === 'Authenticator')
     },
     pleromaAuthenticatorData() {
-      return this.settings.settings[':pleroma']['Pleroma.Web.Auth.Authenticator']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Web.Auth.Authenticator']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/AutoLinker.vue b/src/views/settings/components/AutoLinker.vue
index 29c1f69b2a9dd21daf15f32028144988709f82fd..e5fb7529ef7821dd7bfc9bc5c50a921289099656 100644
--- a/src/views/settings/components/AutoLinker.vue
+++ b/src/views/settings/components/AutoLinker.vue
@@ -11,6 +11,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'AutoLinker',
@@ -23,13 +24,13 @@ export default {
       return this.settings.description.find(setting => setting.key === ':opts')
     },
     autoLinkerData() {
-      return this.settings.settings[':auto_linker'][':opts']
+      return _.get(this.settings.settings, [':auto_linker', ':opts']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
diff --git a/src/views/settings/components/Captcha.vue b/src/views/settings/components/Captcha.vue
index 0a61f0bf6d4b7b1664437937aecec18eb8e729af..5a942793899e054d51b07149f040919bc6741088 100644
--- a/src/views/settings/components/Captcha.vue
+++ b/src/views/settings/components/Captcha.vue
@@ -17,6 +17,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Captcha',
@@ -29,7 +30,7 @@ export default {
       return this.settings.description.find(setting => setting.key === 'Pleroma.Captcha')
     },
     captchaData() {
-      return this.settings.settings[':pleroma']['Pleroma.Captcha']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Captcha']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
@@ -38,10 +39,10 @@ export default {
       return this.settings.description.find(setting => setting.key === 'Pleroma.Captcha.Kocaptcha')
     },
     kocaptchaData() {
-      return this.settings.settings[':pleroma']['Pleroma.Captcha.Kocaptcha']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Captcha.Kocaptcha']) || {}
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
diff --git a/src/views/settings/components/Database.vue b/src/views/settings/components/Database.vue
deleted file mode 100644
index 773472f1d1c7ad6199ca620efcb68592e365f112..0000000000000000000000000000000000000000
--- a/src/views/settings/components/Database.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-<template>
-  <div v-if="!loading">
-    <el-form ref="databaseData" :model="databaseData" :label-width="labelWidth">
-      <setting :setting-group="database" :data="databaseData"/>
-      <el-form-item>
-        <el-button type="primary" @click="onSubmit">Submit</el-button>
-      </el-form-item>
-    </el-form>
-  </div>
-</template>
-
-<script>
-import { mapGetters } from 'vuex'
-import i18n from '@/lang'
-import Setting from './Setting'
-
-export default {
-  name: 'Database',
-  components: { Setting },
-  computed: {
-    ...mapGetters([
-      'settings'
-    ]),
-    database() {
-      return this.settings.description.find(setting => setting.key === ':database')
-    },
-    databaseData() {
-      return this.settings.settings[':pleroma'][':database']
-    },
-    isMobile() {
-      return this.$store.state.app.device === 'mobile'
-    },
-    labelWidth() {
-      return this.isMobile ? '100px' : '240px'
-    },
-    loading() {
-      return this.settings.loading
-    }
-  },
-  methods: {
-    async onSubmit() {
-      try {
-        await this.$store.dispatch('SubmitChanges')
-      } catch (e) {
-        return
-      }
-      this.$message({
-        type: 'success',
-        message: i18n.t('settings.success')
-      })
-    }
-  }
-}
-</script>
-
-<style rel='stylesheet/scss' lang='scss'>
-@import '../styles/main';
-@include settings
-</style>
diff --git a/src/views/settings/components/Endpoint.vue b/src/views/settings/components/Endpoint.vue
deleted file mode 100644
index 6becdbb2f6acc5821fca99a9bdc0f7eb942e17be..0000000000000000000000000000000000000000
--- a/src/views/settings/components/Endpoint.vue
+++ /dev/null
@@ -1,81 +0,0 @@
-<template>
-  <div v-if="!loading">
-    <el-form ref="endpointData" :model="endpointData" :label-width="labelWidth">
-      <setting :setting-group="endpoint" :data="endpointData"/>
-    </el-form>
-    <div class="line"/>
-    <el-form ref="endpointMetricsExporter" :model="endpointMetricsExporterData" :label-width="labelWidth">
-      <setting :setting-group="endpointMetricsExporter" :data="endpointMetricsExporterData"/>
-    </el-form>
-    <div class="line"/>
-    <el-form ref="remoteIp" :model="remoteIpData" :label-width="labelWidth">
-      <setting :setting-group="remoteIp" :data="remoteIpData"/>
-      <el-form-item>
-        <el-button type="primary" @click="onSubmit">Submit</el-button>
-      </el-form-item>
-    </el-form>
-  </div>
-</template>
-
-<script>
-import { mapGetters } from 'vuex'
-import i18n from '@/lang'
-import Setting from './Setting'
-
-export default {
-  name: 'Endpoint',
-  components: {
-    Setting
-  },
-  computed: {
-    ...mapGetters([
-      'settings'
-    ]),
-    endpoint() {
-      return this.settings.description.find(setting => setting.key === 'Pleroma.Web.Endpoint')
-    },
-    endpointData() {
-      return this.settings.settings[':pleroma']['Pleroma.Web.Endpoint']
-    },
-    endpointMetricsExporter() {
-      return this.settings.description.find(setting => setting.key === 'Pleroma.Web.Endpoint.MetricsExporter')
-    },
-    endpointMetricsExporterData() {
-      return this.settings.settings[':prometheus']['Pleroma.Web.Endpoint.MetricsExporter']
-    },
-    isMobile() {
-      return this.$store.state.app.device === 'mobile'
-    },
-    labelWidth() {
-      return this.isMobile ? '100px' : '240px'
-    },
-    loading() {
-      return this.settings.loading
-    },
-    remoteIp() {
-      return this.settings.description.find(setting => setting.key === 'Pleroma.Plugs.RemoteIp')
-    },
-    remoteIpData() {
-      return this.settings.settings[':pleroma']['Pleroma.Plugs.RemoteIp']
-    }
-  },
-  methods: {
-    async onSubmit() {
-      try {
-        await this.$store.dispatch('SubmitChanges')
-      } catch (e) {
-        return
-      }
-      this.$message({
-        type: 'success',
-        message: i18n.t('settings.success')
-      })
-    }
-  }
-}
-</script>
-
-<style rel='stylesheet/scss' lang='scss'>
-@import '../styles/main';
-@include settings
-</style>
diff --git a/src/views/settings/components/Esshd.vue b/src/views/settings/components/Esshd.vue
index c66ec01b35e5c9f1a429a4dbaecba2e489a5e5ad..6ca3ae15e3b2ecfad150a734789af273e6d02eec 100644
--- a/src/views/settings/components/Esshd.vue
+++ b/src/views/settings/components/Esshd.vue
@@ -20,6 +20,7 @@
 import i18n from '@/lang'
 import { mapGetters } from 'vuex'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Esshd',
@@ -32,13 +33,13 @@ export default {
       return this.settings.description.find(setting => setting.group === ':esshd')
     },
     esshdData() {
-      return this.settings.settings[':esshd']
+      return _.get(this.settings.settings, [':esshd']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
diff --git a/src/views/settings/components/Frontend.vue b/src/views/settings/components/Frontend.vue
index fb969532cbd471e301b95bc37b5b592e5312b5d8..72c236a8304e19a95985356ef1d26cdc48fbc527 100644
--- a/src/views/settings/components/Frontend.vue
+++ b/src/views/settings/components/Frontend.vue
@@ -1,11 +1,6 @@
 <template>
   <div v-if="!loading">
     <el-form ref="frontendData" :model="frontendData" :label-width="labelWidth">
-      <el-form-item>
-        <p class="expl">This form can be used to configure a keyword list that keeps the configuration data for any kind of frontend.
-        By default, settings for <span class="code">pleroma_fe</span> and <span class="code">masto_fe</span> are configured.
-        If you want to add your own configuration your settings need to be complete as they will override the defaults.</p>
-      </el-form-item>
       <setting :setting-group="frontend" :data="frontendData"/>
     </el-form>
     <el-form ref="assetsData" :model="assetsData" :label-width="labelWidth">
@@ -36,6 +31,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Frontend',
@@ -48,37 +44,37 @@ export default {
       return this.settings.description.find(setting => setting.key === ':assets')
     },
     assetsData() {
-      return this.settings.settings[':pleroma'][':assets']
+      return _.get(this.settings.settings, [':pleroma', ':assets']) || {}
     },
     chat() {
       return this.settings.description.find(setting => setting.key === ':chat')
     },
     chatData() {
-      return this.settings.settings[':pleroma'][':chat']
+      return _.get(this.settings.settings, [':pleroma', ':chat']) || {}
     },
     emoji() {
       return this.settings.description.find(setting => setting.key === ':emoji')
     },
     emojiData() {
-      return this.settings.settings[':pleroma'][':emoji']
+      return _.get(this.settings.settings, [':pleroma', ':emoji']) || {}
     },
     frontend() {
       return this.settings.description.find(setting => setting.key === ':frontend_configurations')
     },
     frontendData() {
-      return this.settings.settings[':pleroma'][':frontend_configurations']
+      return _.get(this.settings.settings, [':pleroma', ':frontend_configurations']) || {}
     },
     markup() {
       return this.settings.description.find(setting => setting.key === ':markup')
     },
     markupData() {
-      return this.settings.settings[':pleroma'][':markup']
+      return _.get(this.settings.settings, [':pleroma', ':markup']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
diff --git a/src/views/settings/components/Gopher.vue b/src/views/settings/components/Gopher.vue
index 80d643400ea19a68c53b7392837b0ec69e5f5496..97b531fc7407f2218732647e9ad7ba7b41ad17ae 100644
--- a/src/views/settings/components/Gopher.vue
+++ b/src/views/settings/components/Gopher.vue
@@ -11,6 +11,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Gopher',
@@ -23,13 +24,13 @@ export default {
       return this.settings.description.find(setting => setting.key === ':gopher')
     },
     gopherData() {
-      return this.settings.settings[':pleroma'][':gopher']
+      return _.get(this.settings.settings, [':pleroma', ':gopher']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
diff --git a/src/views/settings/components/Http.vue b/src/views/settings/components/Http.vue
index a553657de0796f4eb1a8a12f39f864adb81e1a05..7c6292ead48028c29d2282291008649f8262314c 100644
--- a/src/views/settings/components/Http.vue
+++ b/src/views/settings/components/Http.vue
@@ -29,6 +29,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'HTTP',
@@ -41,31 +42,31 @@ export default {
       return this.settings.description.find(setting => setting.group === ':cors_plug')
     },
     corsPlugData() {
-      return this.settings.settings[':cors_plug']
+      return _.get(this.settings.settings, [':cors_plug']) || {}
     },
     http() {
       return this.settings.description.find(setting => setting.key === ':http')
     },
     httpData() {
-      return this.settings.settings[':pleroma'][':http']
+      return _.get(this.settings.settings, [':pleroma', ':http']) || {}
     },
     httpSecurity() {
       return this.settings.description.find(setting => setting.key === ':http_security')
     },
     httpSecurityData() {
-      return this.settings.settings[':pleroma'][':http_security']
+      return _.get(this.settings.settings, [':pleroma', ':http_security']) || {}
     },
     httpSignatures() {
       return this.settings.description.find(setting => setting.group === ':http_signatures')
     },
     httpSignaturesData() {
-      return this.settings.settings[':http_signatures']
+      return _.get(this.settings.settings, [':http_signatures']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
@@ -74,7 +75,7 @@ export default {
       return this.settings.description.find(setting => setting.key === ':web_cache_ttl')
     },
     webCacheTtlData() {
-      return this.settings.settings[':pleroma'][':web_cache_ttl']
+      return _.get(this.settings.settings, [':pleroma', ':web_cache_ttl']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/Inputs.vue b/src/views/settings/components/Inputs.vue
index cfb6d09d793794fb2e042593e5efb2ef34fcb577..760d2c2cd847989d97cd2eb8cbce790a1f0458c8 100644
--- a/src/views/settings/components/Inputs.vue
+++ b/src/views/settings/components/Inputs.vue
@@ -1,7 +1,13 @@
 <template>
-  <el-form-item :label="setting.label" :label-width="customLabelWidth" :class="labelClass">
+  <el-form-item :label-width="customLabelWidth" :class="labelClass">
+    <span slot="label">
+      {{ setting.label }}
+      <el-tooltip v-if="canBeDeleted" :content="$t('settings.removeFromDB')" placement="bottom-end">
+        <el-button icon="el-icon-delete" circle size="mini" style="margin-left:5px" @click="removeSetting"/>
+      </el-tooltip>
+    </span>
     <el-input
-      v-if="setting.type === 'string'"
+      v-if="setting.type === 'string' || (setting.type.includes('string') && setting.type.includes('atom'))"
       :value="inputValue"
       :placeholder="setting.suggestions ? setting.suggestions[0] : null"
       @input="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)"/>
@@ -36,13 +42,6 @@
       @change="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)">
       <el-option v-for="(option, index) in setting.suggestions" :key="index" :value="option"/>
     </el-select>
-    <editor
-      v-if="setting.key === ':dispatch'"
-      v-model="editorContent"
-      height="150"
-      width="100%"
-      lang="elixir"
-      theme="chrome"/>
     <el-input
       v-if="setting.key === ':ip'"
       :value="inputValue"
@@ -62,7 +61,7 @@
           :setting-parent="[...settingParent, subSetting]"
           :setting="subSetting"
           :data="data[setting.key]"
-          :custom-label-width="'100px'"
+          :custom-label-width="'140px'"
           :label-class="'center-label'"
           :input-class="'keyword-inner-input'"
           :nested="true"/>
@@ -70,11 +69,10 @@
     </div>
     <!-- special inputs -->
     <auto-linker-input v-if="settingGroup.group === ':auto_linker'" :data="data" :setting-group="settingGroup" :setting="setting"/>
-    <mascots-input v-if="setting.key === ':mascots'" :data="data" :setting-group="settingGroup" :setting="setting"/>
-    <editable-keyword-input v-if="editableKeyword(setting.key, setting.type)" :data="data" :setting-group="settingGroup" :setting="setting"/>
-    <icons-input v-if="setting.key === ':icons'" :data="data[':icons']" :setting-group="settingGroup" :setting="setting"/>
+    <mascots-input v-if="setting.key === ':mascots'" :data="keywordData" :setting-group="settingGroup" :setting="setting"/>
+    <editable-keyword-input v-if="editableKeyword(setting.key, setting.type)" :data="keywordData" :setting-group="settingGroup" :setting="setting"/>
+    <icons-input v-if="setting.key === ':icons'" :data="iconsData" :setting-group="settingGroup" :setting="setting"/>
     <proxy-url-input v-if="setting.key === ':proxy_url'" :data="data[setting.key]" :setting-group="settingGroup" :setting="setting" :parents="settingParent"/>
-    <!-- <ssl-options-input v-if="setting.key === ':ssl_options'" :setting-group="settingGroup" :setting-parent="settingParent" :setting="setting" :data="data" :nested="true" :custom-label-width="'100px'"/> -->
     <multiple-select v-if="setting.key === ':backends' || setting.key === ':args'" :data="data" :setting-group="settingGroup" :setting="setting"/>
     <prune-input v-if="setting.key === ':prune'" :data="data[setting.key]" :setting-group="settingGroup" :setting="setting"/>
     <rate-limit-input v-if="settingGroup.key === ':rate_limit'" :data="data" :setting-group="settingGroup" :setting="setting"/>
@@ -84,16 +82,14 @@
 </template>
 
 <script>
-import AceEditor from 'vue2-ace-editor'
-import 'brace/mode/elixir'
-import 'default-passive-events'
-import { AutoLinkerInput, EditableKeywordInput, IconsInput, MascotsInput, MultipleSelect, ProxyUrlInput, PruneInput, RateLimitInput, SslOptionsInput } from './inputComponents'
+import i18n from '@/lang'
+import { AutoLinkerInput, EditableKeywordInput, IconsInput, MascotsInput, MultipleSelect, ProxyUrlInput, PruneInput, RateLimitInput } from './inputComponents'
 import { processNested } from '@/store/modules/normalizers'
+import _ from 'lodash'
 
 export default {
   name: 'Inputs',
   components: {
-    editor: AceEditor,
     AutoLinkerInput,
     EditableKeywordInput,
     IconsInput,
@@ -101,8 +97,7 @@ export default {
     MultipleSelect,
     ProxyUrlInput,
     PruneInput,
-    RateLimitInput,
-    SslOptionsInput
+    RateLimitInput
   },
   props: {
     customLabelWidth: {
@@ -159,13 +154,13 @@ export default {
     }
   },
   computed: {
-    editorContent: {
-      get: function() {
-        return this.data[this.setting.key] ? this.data[this.setting.key][0] : ''
-      },
-      set: function(value) {
-        this.processNestedData([value], this.settingGroup.group, this.settingGroup.key, this.settingParent)
-      }
+    canBeDeleted() {
+      const { group, key } = this.settingGroup
+      return _.get(this.$store.state.settings.db, [group, key]) &&
+        this.$store.state.settings.db[group][key].includes(this.setting.key)
+    },
+    iconsData() {
+      return Array.isArray(this.data[':icons']) ? this.data[':icons'] : []
     },
     inputValue() {
       if ([':esshd', ':cors_plug', ':quack', ':http_signatures', ':tesla'].includes(this.settingGroup.group) &&
@@ -178,7 +173,7 @@ export default {
         this.setting.key === ':admin_token') {
         return this.data.value
       } else if (this.settingGroup.group === ':mime' && this.settingParent[0].key === ':types') {
-        return this.data.value[this.setting.key]
+        return this.data.value ? this.data.value[this.setting.key] : []
       } else if (this.setting.type === 'atom') {
         return this.data[this.setting.key] && this.data[this.setting.key][0] === ':' ? this.data[this.setting.key].substr(1) : this.data[this.setting.key]
       } else {
@@ -186,7 +181,10 @@ export default {
       }
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
+    },
+    keywordData() {
+      return Array.isArray(this.data) ? this.data : []
     },
     rewritePolicyValue() {
       return typeof this.data[this.setting.key] === 'string' ? [this.data[this.setting.key]] : this.data[this.setting.key]
@@ -215,6 +213,20 @@ export default {
       this.$store.dispatch('UpdateState',
         { group, key: parentKey, input: setting.key, value: valueForState })
     },
+    async removeSetting() {
+      const config = this.settingGroup.key
+        ? [{ group: this.settingGroup.group, key: this.settingGroup.key, delete: true, subkeys: [this.setting.key] }]
+        : [{ group: this.settingGroup.group, key: this.setting.key, delete: true }]
+      try {
+        await this.$store.dispatch('RemoveSetting', config)
+      } catch (e) {
+        return
+      }
+      this.$message({
+        type: 'success',
+        message: i18n.t('settings.successfullyRemoved')
+      })
+    },
     renderMultipleSelect(type) {
       return Array.isArray(type) && this.setting.key !== ':backends' && this.setting.key !== ':args' && (
         type.includes('module') ||
diff --git a/src/views/settings/components/Instance.vue b/src/views/settings/components/Instance.vue
index a5af7f9fa66268e100ef7460a13cd651dfd7de77..5a8350bff60f4d81ef3b9d785b4fca3a11936dc1 100644
--- a/src/views/settings/components/Instance.vue
+++ b/src/views/settings/components/Instance.vue
@@ -42,6 +42,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Instance',
@@ -56,25 +57,25 @@ export default {
       return this.settings.description.find(setting => setting.description === `Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the 'admin_token' parameter`)
     },
     adminTokenData() {
-      return this.settings.settings[':pleroma'][':admin_token']
+      return _.get(this.settings.settings, [':pleroma', ':admin_token']) || {}
     },
     fetchInitialPosts() {
       return this.settings.description.find(setting => setting.key === ':fetch_initial_posts')
     },
     fetchInitialPostsData() {
-      return this.settings.settings[':pleroma'][':fetch_initial_posts']
+      return _.get(this.settings.settings, [':pleroma', ':fetch_initial_posts']) || {}
     },
     instance() {
       return this.settings.description.find(setting => setting.key === ':instance')
     },
     instanceData() {
-      return this.settings.settings[':pleroma'][':instance']
+      return _.get(this.settings.settings, [':pleroma', ':instance']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
@@ -83,31 +84,31 @@ export default {
       return this.settings.description.find(setting => setting.key === ':manifest')
     },
     manifestData() {
-      return this.settings.settings[':pleroma'][':manifest']
+      return _.get(this.settings.settings, [':pleroma', ':manifest']) || {}
     },
     pleromaUser() {
       return this.settings.description.find(setting => setting.key === 'Pleroma.User')
     },
     pleromaUserData() {
-      return this.settings.settings[':pleroma']['Pleroma.User']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.User']) || {}
     },
     scheduledActivity() {
       return this.$store.state.settings.description.find(setting => setting.key === 'Pleroma.ScheduledActivity')
     },
     scheduledActivityData() {
-      return this.settings.settings[':pleroma']['Pleroma.ScheduledActivity']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.ScheduledActivity']) || {}
     },
     suggestions() {
       return this.$store.state.settings.description.find(setting => setting.key === ':suggestions')
     },
     suggestionsData() {
-      return this.settings.settings[':pleroma'][':suggestions']
+      return _.get(this.settings.settings, [':pleroma', ':suggestions']) || {}
     },
     uriSchemes() {
       return this.$store.state.settings.description.find(setting => setting.key === ':uri_schemes')
     },
     uriSchemesData() {
-      return this.settings.settings[':pleroma'][':uri_schemes']
+      return _.get(this.settings.settings, [':pleroma', ':uri_schemes']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/JobQueue.vue b/src/views/settings/components/JobQueue.vue
index 1e31e5365e5ebdca10330393a969dcb73fec852f..41a11015746d787a83acefe576d1d35c3e65f282 100644
--- a/src/views/settings/components/JobQueue.vue
+++ b/src/views/settings/components/JobQueue.vue
@@ -19,6 +19,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'JobQueue',
@@ -31,13 +32,13 @@ export default {
       return this.settings.description.find(setting => setting.key === 'Pleroma.ActivityExpiration')
     },
     activityExpirationData() {
-      return this.settings.settings[':pleroma']['Pleroma.ActivityExpiration']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.ActivityExpiration']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
@@ -46,13 +47,13 @@ export default {
       return this.settings.description.find(setting => setting.key === 'Oban')
     },
     obanQueuesData() {
-      return this.settings.settings[':pleroma']['Oban']
+      return _.get(this.settings.settings, [':pleroma', 'Oban']) || {}
     },
     workers() {
       return this.settings.description.find(setting => setting.key === ':workers')
     },
     workersData() {
-      return this.settings.settings[':pleroma'][':workers']
+      return _.get(this.settings.settings, [':pleroma', ':workers']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/Logger.vue b/src/views/settings/components/Logger.vue
index f8f4ed323155ace307961f9252dce610b5efc111..0dd308a416d8b6982c7128b188fbc6ea98d15c2e 100644
--- a/src/views/settings/components/Logger.vue
+++ b/src/views/settings/components/Logger.vue
@@ -26,6 +26,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Logger',
@@ -38,19 +39,19 @@ export default {
       return this.settings.description.find(setting => setting.key === ':console')
     },
     consoleData() {
-      return this.settings.settings[':logger'][':console']
+      return _.get(this.settings.settings, [':logger', ':console']) || {}
     },
     exsyslogger() {
       return this.settings.description.find(setting => setting.key === ':ex_syslogger')
     },
     exsysloggerData() {
-      return this.settings.settings[':logger'][':ex_syslogger']
+      return _.get(this.settings.settings, [':logger', ':ex_syslogger']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
@@ -59,13 +60,13 @@ export default {
       return this.settings.description.find(setting => setting.group === ':logger')
     },
     loggerData() {
-      return this.settings.settings[':logger'][':backends']
+      return _.get(this.settings.settings, [':logger', ':backends']) || {}
     },
     quack() {
       return this.settings.description.find(setting => setting.group === ':quack')
     },
     quackData() {
-      return this.settings.settings[':quack']
+      return _.get(this.settings.settings, [':quack']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/MRF.vue b/src/views/settings/components/MRF.vue
index fd3be846982d7c65f437c8ddab8a4fe4db7494e9..3c1584cc0a312d68dc1cd80402403756c2f5d248 100644
--- a/src/views/settings/components/MRF.vue
+++ b/src/views/settings/components/MRF.vue
@@ -39,6 +39,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'MRF',
@@ -51,7 +52,7 @@ export default {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
@@ -60,49 +61,49 @@ export default {
       return this.settings.description.find(setting => setting.key === ':mrf_simple')
     },
     mrfSimpleData() {
-      return this.settings.settings[':pleroma'][':mrf_simple']
+      return _.get(this.settings.settings, [':pleroma', ':mrf_simple']) || {}
     },
     mrfRejectnonpublic() {
       return this.settings.description.find(setting => setting.key === ':mrf_rejectnonpublic')
     },
     mrfRejectnonpublicData() {
-      return this.settings.settings[':pleroma'][':mrf_rejectnonpublic']
+      return _.get(this.settings.settings, [':pleroma', ':mrf_rejectnonpublic']) || {}
     },
     mrfHellthread() {
       return this.settings.description.find(setting => setting.key === ':mrf_hellthread')
     },
     mrfHellthreadData() {
-      return this.settings.settings[':pleroma'][':mrf_hellthread']
+      return _.get(this.settings.settings, [':pleroma', ':mrf_hellthread']) || {}
     },
     mrfKeyword() {
       return this.settings.description.find(setting => setting.key === ':mrf_keyword')
     },
     mrfKeywordData() {
-      return this.settings.settings[':pleroma'][':mrf_keyword']
+      return _.get(this.settings.settings, [':pleroma', ':mrf_keyword']) || {}
     },
     mrfSubchain() {
       return this.settings.description.find(setting => setting.key === ':mrf_subchain')
     },
     mrfSubchainData() {
-      return this.settings.settings[':pleroma'][':mrf_subchain']
+      return _.get(this.settings.settings, [':pleroma', ':mrf_subchain']) || {}
     },
     mrfMention() {
       return this.settings.description.find(setting => setting.key === ':mrf_mention')
     },
     mrfMentionData() {
-      return this.settings.settings[':pleroma'][':mrf_mention']
+      return _.get(this.settings.settings, [':pleroma', ':mrf_mention']) || {}
     },
     mrfNormalizeMarkup() {
       return this.settings.description.find(setting => setting.key === ':mrf_normalize_markup')
     },
     mrfNormalizeMarkupData() {
-      return this.settings.settings[':pleroma'][':mrf_normalize_markup']
+      return _.get(this.settings.settings, [':pleroma', ':mrf_normalize_markup']) || {}
     },
     mrfVocabulary() {
       return this.settings.description.find(setting => setting.key === ':mrf_vocabulary')
     },
     mrfVocabularyData() {
-      return this.settings.settings[':pleroma'][':mrf_vocabulary']
+      return _.get(this.settings.settings, [':pleroma', ':mrf_vocabulary']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/Mailer.vue b/src/views/settings/components/Mailer.vue
index d3215692a01bb0132184fa55d5d456fd23d2e95b..78be7eabaf1e81226bbab91efd6eea51bd8ea7d5 100644
--- a/src/views/settings/components/Mailer.vue
+++ b/src/views/settings/components/Mailer.vue
@@ -20,6 +20,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Mailer',
@@ -34,13 +35,13 @@ export default {
       return this.settings.description.find(setting => setting.key === ':email_notifications')
     },
     emailNotificationsData() {
-      return this.settings.settings[':pleroma'][':email_notifications']
+      return _.get(this.settings.settings, [':pleroma', ':email_notifications']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.$store.state.settings.loading
@@ -49,13 +50,13 @@ export default {
       return this.settings.description.find(setting => setting.key === 'Pleroma.Emails.Mailer')
     },
     mailerData() {
-      return this.settings.settings[':pleroma']['Pleroma.Emails.Mailer']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Emails.Mailer']) || {}
     },
     userEmail() {
       return this.settings.description.find(setting => setting.key === 'Pleroma.Emails.UserEmail')
     },
     userEmailData() {
-      return this.settings.settings[':pleroma']['Pleroma.Emails.UserEmail']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Emails.UserEmail']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/MediaProxy.vue b/src/views/settings/components/MediaProxy.vue
index dac2ea7d6bf133214082e5fa7ca1ac5ecfc00eee..0811df50fd52af54e76c2752a26c4e699a66e8a2 100644
--- a/src/views/settings/components/MediaProxy.vue
+++ b/src/views/settings/components/MediaProxy.vue
@@ -11,6 +11,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'MediaProxy',
@@ -23,7 +24,7 @@ export default {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
@@ -32,7 +33,7 @@ export default {
       return this.settings.description.find(setting => setting.key === ':media_proxy')
     },
     mediaProxyData() {
-      return this.settings.settings[':pleroma'][':media_proxy']
+      return _.get(this.settings.settings, [':pleroma', ':media_proxy']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/Metadata.vue b/src/views/settings/components/Metadata.vue
index a13d676bd7be4ec649ac58e6b0242373508ec440..ff4b044819f24fefda1346d4688d0e22ad53a7c4 100644
--- a/src/views/settings/components/Metadata.vue
+++ b/src/views/settings/components/Metadata.vue
@@ -17,6 +17,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Metadata',
@@ -29,7 +30,7 @@ export default {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
@@ -38,13 +39,13 @@ export default {
       return this.settings.description.find(setting => setting.key === 'Pleroma.Web.Metadata')
     },
     metadataData() {
-      return this.settings.settings[':pleroma']['Pleroma.Web.Metadata']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Web.Metadata']) || {}
     },
     richMedia() {
       return this.settings.description.find(setting => setting.key === ':rich_media')
     },
     richMediaData() {
-      return this.settings.settings[':pleroma'][':rich_media']
+      return _.get(this.settings.settings, [':pleroma', ':rich_media']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/Other.vue b/src/views/settings/components/Other.vue
index 466dc3a1245e03c21709931fafbf5896c7ece1ad..ebb21d51d33a3e034e5ffa9a28faec182dcb5375 100644
--- a/src/views/settings/components/Other.vue
+++ b/src/views/settings/components/Other.vue
@@ -6,6 +6,9 @@
     <div class="line"/>
     <el-form ref="mimeTypes" :model="mimeTypesData" :label-width="labelWidth">
       <setting :setting-group="mimeTypes" :data="mimeTypesData"/>
+    </el-form>
+    <el-form ref="remoteIp" :model="remoteIpData" :label-width="labelWidth">
+      <setting :setting-group="remoteIp" :data="remoteIpData"/>
       <el-form-item>
         <el-button type="primary" @click="onSubmit">Submit</el-button>
       </el-form-item>
@@ -17,6 +20,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Other',
@@ -29,7 +33,7 @@ export default {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
@@ -38,13 +42,19 @@ export default {
       return this.settings.description.find(setting => setting.group === ':mime')
     },
     mimeTypesData() {
-      return this.settings.settings[':mime']
+      return _.get(this.settings.settings, [':mime']) || {}
+    },
+    remoteIp() {
+      return this.settings.description.find(setting => setting.key === 'Pleroma.Plugs.RemoteIp')
+    },
+    remoteIpData() {
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Plugs.RemoteIp']) || {}
     },
     teslaAdapter() {
       return this.settings.description.find(setting => setting.group === ':tesla')
     },
     teslaAdapterData() {
-      return this.settings.settings[':tesla']
+      return _.get(this.settings.settings, [':tesla']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/RateLimiters.vue b/src/views/settings/components/RateLimiters.vue
index cfdeca155ad486af8880540146a3252079345bfc..224d22534f94599f70e21bc7d1773716367214bc 100644
--- a/src/views/settings/components/RateLimiters.vue
+++ b/src/views/settings/components/RateLimiters.vue
@@ -11,6 +11,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'RateLimiters',
@@ -23,13 +24,13 @@ export default {
       return this.settings.description.find(setting => setting.key === ':rate_limit')
     },
     rateLimitersData() {
-      return this.settings.settings[':pleroma'][':rate_limit']
+      return _.get(this.settings.settings, [':pleroma', ':rate_limit']) || {}
     },
     isMobile() {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.$store.state.settings.loading
diff --git a/src/views/settings/components/Setting.vue b/src/views/settings/components/Setting.vue
index d11dc723d9bbd449ee6ecd3725834f5bce49bb6b..fb7b2252bbf3f2487c17667dd45501e8cf7b360d 100644
--- a/src/views/settings/components/Setting.vue
+++ b/src/views/settings/components/Setting.vue
@@ -29,17 +29,30 @@
             :nested="false"/>
         </div>
         <div v-if="compound(setting)">
-          <el-form-item :label="`${setting.label}:`"/>
-          <div v-for="subSetting in setting.children" :key="subSetting.key">
+          <div v-if="!setting.children">
             <inputs
               :setting-group="settingGroup"
-              :setting-parent="[setting, subSetting]"
-              :setting="subSetting"
+              :setting="setting"
               :data="data[setting.key]"
               :nested="true"/>
           </div>
-          <div v-if="!setting.children">
-            <inputs :setting-group="settingGroup" :setting="setting" :data="data[setting.key]" :nested="true"/>
+          <div v-else>
+            <el-form-item>
+              <span slot="label">
+                {{ setting.label }}:
+                <el-tooltip v-if="canBeDeleted(setting.key)" :content="$t('settings.removeFromDB')" placement="bottom-end">
+                  <el-button icon="el-icon-delete" circle size="mini" style="margin-left:5px" @click="removeSetting(setting.key)"/>
+                </el-tooltip>
+              </span>
+            </el-form-item>
+            <div v-for="subSetting in setting.children" :key="subSetting.key">
+              <inputs
+                :setting-group="settingGroup"
+                :setting-parent="[setting, subSetting]"
+                :setting="subSetting"
+                :data="data[setting.key]"
+                :nested="true"/>
+            </div>
           </div>
           <div class="line"/>
         </div>
@@ -49,15 +62,13 @@
 </template>
 
 <script>
-import AceEditor from 'vue2-ace-editor'
 import Inputs from './Inputs'
-import 'brace/mode/elixir'
-import 'default-passive-events'
+import i18n from '@/lang'
+import _ from 'lodash'
 
 export default {
   name: 'Setting',
   components: {
-    editor: AceEditor,
     Inputs
   },
   props: {
@@ -84,12 +95,32 @@ export default {
     }
   },
   methods: {
+    canBeDeleted(settingKey) {
+      const { group, key } = this.settingGroup
+      const existingKey = key || settingKey
+      return _.get(this.$store.state.settings.db, [group, existingKey]) &&
+        this.$store.state.settings.db[group][existingKey].includes(settingKey)
+    },
     compound({ type, key, children }) {
       return type === 'keyword' ||
         type === 'map' ||
         type.includes('keyword') ||
         key === ':replace'
     },
+    async removeSetting(key) {
+      const config = this.settingGroup.key
+        ? [{ group: this.settingGroup.group, key: this.settingGroup.key, delete: true, subkeys: [key] }]
+        : [{ group: this.settingGroup.group, key, delete: true }]
+      try {
+        await this.$store.dispatch('RemoveSetting', config)
+      } catch (e) {
+        return
+      }
+      this.$message({
+        type: 'success',
+        message: i18n.t('settings.successfullyRemoved')
+      })
+    },
     updateSetting(value, tab, input) {
       this.$store.dispatch('UpdateSettings', { tab, data: { [input]: value }})
     }
diff --git a/src/views/settings/components/Upload.vue b/src/views/settings/components/Upload.vue
index 546456df13fcfd3f127405eb7d11ce74c38aade1..b31990b23df647a04c8a36eb25b7abc77053ecdd 100644
--- a/src/views/settings/components/Upload.vue
+++ b/src/views/settings/components/Upload.vue
@@ -11,10 +11,6 @@
       <setting :setting-group="uploadersS3" :data="uploadersS3Data"/>
     </el-form>
     <div class="line"/>
-    <el-form ref="uploadersMDII" :model="uploadersMDIIData" :label-width="labelWidth">
-      <setting :setting-group="uploadersMDII" :data="uploadersMDIIData"/>
-    </el-form>
-    <div class="line"/>
     <el-form ref="uploadFilterMogrify" :model="uploadFilterMogrifyData" :label-width="labelWidth">
       <setting :setting-group="uploadFilterMogrify" :data="uploadFilterMogrifyData"/>
     </el-form>
@@ -32,6 +28,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'Upload',
@@ -44,7 +41,7 @@ export default {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
@@ -53,37 +50,31 @@ export default {
       return this.settings.description.find(setting => setting.key === 'Pleroma.Upload')
     },
     uploadData() {
-      return this.settings.settings[':pleroma']['Pleroma.Upload']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Upload']) || {}
     },
     uploadersLocal() {
       return this.settings.description.find(setting => setting.key === 'Pleroma.Uploaders.Local')
     },
     uploadersLocalData() {
-      return this.settings.settings[':pleroma']['Pleroma.Uploaders.Local']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Uploaders.Local']) || {}
     },
     uploadersS3() {
       return this.settings.description.find(setting => setting.key === 'Pleroma.Uploaders.S3')
     },
     uploadersS3Data() {
-      return this.settings.settings[':pleroma']['Pleroma.Uploaders.S3']
-    },
-    uploadersMDII() {
-      return this.settings.description.find(setting => setting.key === 'Pleroma.Uploaders.MDII')
-    },
-    uploadersMDIIData() {
-      return this.settings.settings[':pleroma']['Pleroma.Uploaders.MDII']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Uploaders.S3']) || {}
     },
     uploadFilterMogrify() {
       return this.settings.description.find(setting => setting.key === 'Pleroma.Upload.Filter.Mogrify')
     },
     uploadFilterMogrifyData() {
-      return this.settings.settings[':pleroma']['Pleroma.Upload.Filter.Mogrify']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Upload.Filter.Mogrify']) || {}
     },
     uploadAnonymizeFilename() {
       return this.settings.description.find(setting => setting.key === 'Pleroma.Upload.Filter.AnonymizeFilename')
     },
     uploadAnonymizeFilenameData() {
-      return this.settings.settings[':pleroma']['Pleroma.Upload.Filter.AnonymizeFilename']
+      return _.get(this.settings.settings, [':pleroma', 'Pleroma.Upload.Filter.AnonymizeFilename']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/WebPush.vue b/src/views/settings/components/WebPush.vue
index e5f6023927597c99adb470b6a4014dbc1cd57f24..dc03151a5348d0db05d3249fa697fe5c4fe3f297 100644
--- a/src/views/settings/components/WebPush.vue
+++ b/src/views/settings/components/WebPush.vue
@@ -11,6 +11,7 @@
 import { mapGetters } from 'vuex'
 import i18n from '@/lang'
 import Setting from './Setting'
+import _ from 'lodash'
 
 export default {
   name: 'WebPush',
@@ -23,7 +24,7 @@ export default {
       return this.$store.state.app.device === 'mobile'
     },
     labelWidth() {
-      return this.isMobile ? '100px' : '240px'
+      return this.isMobile ? '100px' : '280px'
     },
     loading() {
       return this.settings.loading
@@ -32,7 +33,7 @@ export default {
       return this.settings.description.find(setting => setting.key === ':vapid_details')
     },
     vapidDetailsData() {
-      return this.settings.settings[':web_push_encryption'][':vapid_details']
+      return _.get(this.settings.settings, [':web_push_encryption', ':vapid_details']) || {}
     }
   },
   methods: {
diff --git a/src/views/settings/components/index.js b/src/views/settings/components/index.js
index 040d67120287bb9ef5721d169498a4a163a1ddf0..165f43f18fc1b95b1baff1c3ff4a5a4b12bc1a4b 100644
--- a/src/views/settings/components/index.js
+++ b/src/views/settings/components/index.js
@@ -2,8 +2,6 @@ export { default as ActivityPub } from './ActivityPub'
 export { default as Authentication } from './Authentication'
 export { default as AutoLinker } from './AutoLinker'
 export { default as Captcha } from './Captcha'
-export { default as Database } from './Database'
-export { default as Endpoint } from './Endpoint'
 export { default as Esshd } from './Esshd'
 export { default as Frontend } from './Frontend'
 export { default as Gopher } from './Gopher'
diff --git a/src/views/settings/components/inputComponents/EditableKeywordInput.vue b/src/views/settings/components/inputComponents/EditableKeywordInput.vue
index 9f0d77fc2ac061d7608fc64430503022026b90e7..9489c685322c149f1745b2e86cab3bef632fbb01 100644
--- a/src/views/settings/components/inputComponents/EditableKeywordInput.vue
+++ b/src/views/settings/components/inputComponents/EditableKeywordInput.vue
@@ -32,7 +32,7 @@ export default {
   name: 'EditableKeywordInput',
   props: {
     data: {
-      type: [Object, Array],
+      type: Array,
       default: function() {
         return {}
       }
@@ -95,7 +95,7 @@ export default {
     updateSetting(value, group, key, input, type) {
       const updatedSettings = type !== 'map'
         ? value.reduce((acc, element) => {
-          return { ...acc, [Object.keys(element)[0]]: [['list'], Object.values(element)[0].value] }
+          return { ...acc, [Object.keys(element)[0]]: ['list', Object.values(element)[0].value] }
         }, {})
         : value.reduce((acc, element) => {
           return { ...acc, [Object.keys(element)[0]]: Object.values(element)[0].value }
diff --git a/src/views/settings/components/inputComponents/IconsInput.vue b/src/views/settings/components/inputComponents/IconsInput.vue
index 6f52042563a47b8bc17e4844dcea52605c20e096..ac994fe1847a74819eacf7c19769dbda5ff1fa0e 100644
--- a/src/views/settings/components/inputComponents/IconsInput.vue
+++ b/src/views/settings/components/inputComponents/IconsInput.vue
@@ -28,7 +28,7 @@ export default {
   name: 'EditableKeywordInput',
   props: {
     data: {
-      type: [Object, Array],
+      type: Array,
       default: function() {
         return {}
       }
@@ -45,14 +45,11 @@ export default {
         return {}
       }
     }
-  },
-  computed: {
-
   },
   methods: {
     addIconToIcons() {
       const updatedValue = [...this.data, [{ key: '', value: '', id: this.generateID() }]]
-      this.updateSetting(updatedValue, this.settingGroup.group, this.settingGroup.key, this.setting.key)
+      this.updateSetting(updatedValue, this.settingGroup.group, this.settingGroup.key, this.setting.key, this.setting.type)
     },
     addValueToIcons(index) {
       const updatedValue = this.data.map((icon, i) => {
@@ -61,11 +58,11 @@ export default {
         }
         return icon
       })
-      this.updateSetting(updatedValue, this.settingGroup.group, this.settingGroup.key, this.setting.key)
+      this.updateSetting(updatedValue, this.settingGroup.group, this.settingGroup.key, this.setting.key, this.setting.type)
     },
     deleteIcondRow(index) {
       const filteredValues = this.data.filter((icon, i) => i !== index)
-      this.updateSetting(filteredValues, this.settingGroup.group, this.settingGroup.key, this.setting.key)
+      this.updateSetting(filteredValues, this.settingGroup.group, this.settingGroup.key, this.setting.key, this.setting.type)
     },
     generateID() {
       return `f${(~~(Math.random() * 1e8)).toString(16)}`
diff --git a/src/views/settings/components/inputComponents/MascotsInput.vue b/src/views/settings/components/inputComponents/MascotsInput.vue
index 671a6a37d82e0a263099f497032138aa76139a39..96ec5ca7640a15b9507913f6ccf0febf555ec475 100644
--- a/src/views/settings/components/inputComponents/MascotsInput.vue
+++ b/src/views/settings/components/inputComponents/MascotsInput.vue
@@ -23,7 +23,7 @@ export default {
   name: 'MascotsInput',
   props: {
     data: {
-      type: [Object, Array],
+      type: Array,
       default: function() {
         return {}
       }
@@ -88,7 +88,7 @@ export default {
     updateSetting(value, group, key, input, type) {
       const mascotsWithoutIDs = value.reduce((acc, mascot) => {
         const { id, ...mascotValue } = Object.values(mascot)[0]
-        return { ...acc, [Object.keys(mascot)[0]]: mascotValue }
+        return { ...acc, [Object.keys(mascot)[0]]: ['', mascotValue] }
       }, {})
       this.$store.dispatch('UpdateSettings', { group, key, input, value: mascotsWithoutIDs, type })
       this.$store.dispatch('UpdateState', { group, key, input, value })
diff --git a/src/views/settings/components/inputComponents/RateLimitInput.vue b/src/views/settings/components/inputComponents/RateLimitInput.vue
index 3946b25dd73c20c9e032d07e5f908291120102a5..358dcdf6c1379f48926ff735965c76fd8791d80c 100644
--- a/src/views/settings/components/inputComponents/RateLimitInput.vue
+++ b/src/views/settings/components/inputComponents/RateLimitInput.vue
@@ -5,7 +5,8 @@
         :value="rateLimitAllUsers[0]"
         placeholder="scale"
         class="scale-input"
-        @input="parseRateLimiter($event, setting.key, 'scale', 'oneLimit', rateLimitAllUsers)"/> :
+        @input="parseRateLimiter($event, setting.key, 'scale', 'oneLimit', rateLimitAllUsers)"/>
+      <span>:</span>
       <el-input
         :value="rateLimitAllUsers[1]"
         placeholder="limit"
@@ -17,24 +18,26 @@
       </div>
     </div>
     <div v-if="rateLimitAuthUsers">
-      <el-form-item label="Unauthenticated users:">
+      <el-form-item label="Unauthenticated users:" label-width="180px" class="rate-limit">
         <el-input
           :value="rateLimitUnauthUsers[0]"
           placeholder="scale"
           class="scale-input"
-          @input="parseRateLimiter($event, setting.key, 'scale', 'unauthUsersLimit', [rateLimitUnauthUsers, rateLimitAuthUsers])"/> :
+          @input="parseRateLimiter($event, setting.key, 'scale', 'unauthUsersLimit', [rateLimitUnauthUsers, rateLimitAuthUsers])"/>
+        <span>:</span>
         <el-input
           :value="rateLimitUnauthUsers[1]"
           placeholder="limit"
           class="limit-input"
           @input="parseRateLimiter($event, setting.key, 'limit', 'unauthUsersLimit', [rateLimitUnauthUsers, rateLimitAuthUsers])"/>
       </el-form-item>
-      <el-form-item label="Authenticated users:">
+      <el-form-item label="Authenticated users:" label-width="180px" class="rate-limit">
         <el-input
           :value="rateLimitAuthUsers[0]"
           placeholder="scale"
           class="scale-input"
-          @input="parseRateLimiter($event, setting.key, 'scale', 'authUserslimit', [rateLimitUnauthUsers, rateLimitAuthUsers])"/> :
+          @input="parseRateLimiter($event, setting.key, 'scale', 'authUserslimit', [rateLimitUnauthUsers, rateLimitAuthUsers])"/>
+        <span>:</span>
         <el-input
           :value="rateLimitAuthUsers[1]"
           placeholder="limit"
diff --git a/src/views/settings/components/inputComponents/SslOptionsInput.vue b/src/views/settings/components/inputComponents/SslOptionsInput.vue
deleted file mode 100644
index 970ad9bac256a9b80bde62cc9f62b04655f4550a..0000000000000000000000000000000000000000
--- a/src/views/settings/components/inputComponents/SslOptionsInput.vue
+++ /dev/null
@@ -1,93 +0,0 @@
-<template>
-  <div>
-    <div v-for="subSetting in setting.children" :key="subSetting.key">
-      <el-form-item :label="subSetting.label" :label-width="customLabelWidth">
-        <el-select
-          v-if="subSetting.type.includes('list') && subSetting.type.includes('atom')"
-          :value="subSettingValue(subSetting)"
-          multiple
-          filterable
-          allow-create
-          @change="update($event, subSetting.key)">
-          <el-option v-for="(option, index) in subSetting.suggestions" :key="index" :value="option"/>
-        </el-select>
-        <p class="expl">{{ subSetting.description }}</p>
-      </el-form-item>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'SslOptionsInput',
-  props: {
-    customLabelWidth: {
-      type: String,
-      default: function() {
-        return this.labelWidth
-      },
-      required: false
-    },
-    data: {
-      type: [Object, Array],
-      default: function() {
-        return {}
-      }
-    },
-    nested: {
-      type: Boolean,
-      default: function() {
-        return false
-      }
-    },
-    setting: {
-      type: Object,
-      default: function() {
-        return {}
-      }
-    },
-    settingGroup: {
-      type: Object,
-      default: function() {
-        return {}
-      }
-    },
-    settingParent: {
-      type: Object,
-      default: function() {
-        return {}
-      },
-      required: false
-    }
-  },
-  methods: {
-    inputValue(key) {
-      return this.data[this.setting.key][key]
-    },
-    subSettingValue(subSetting) {
-      return this.data && this.data[this.setting.key] ? this.data[this.setting.key][subSetting.key] : []
-    },
-    update(value, childKey) {
-      const [group, key, parentKey, input] = [this.settingGroup.group, this.settingGroup.key, this.setting.key, this.settingParent.key]
-      const { updatedSettings, description } = this.$store.state.settings
-      const type = description
-        .find(element => element.group === group && element.key === key).children
-        .find(child => child.key === ':adapter').children.find(child => child.key === ':ssl_options').children
-        .find(child => child.key === childKey).type
-
-      const updatedState = { ...this.data, [parentKey]: { ...this.data[parentKey], [childKey]: value }}
-      const updatedSetting = !updatedSettings[group] || !updatedSettings[group][key]
-        ? { [parentKey]: ['keyword', { [childKey]: [type, value] }] }
-        : { ...updatedSettings[group][key][parentKey], [parentKey]: { ...updatedSettings[group][key][parentKey], [childKey]: [type, value] }}
-
-      this.$store.dispatch('UpdateSettings', { group, key, input, value: updatedSetting, type: this.settingParent.type })
-      this.$store.dispatch('UpdateState', { group, key, input, value: updatedState })
-    }
-  }
-}
-</script>
-
-<style rel='stylesheet/scss' lang='scss'>
-@import '../../styles/main';
-@include settings
-</style>
diff --git a/src/views/settings/components/inputComponents/index.js b/src/views/settings/components/inputComponents/index.js
index faf7c1f86f28324c1f95f99f931cdcc0fd32fd18..389b304efeee186f2fc438ddd41aebb633917bcc 100644
--- a/src/views/settings/components/inputComponents/index.js
+++ b/src/views/settings/components/inputComponents/index.js
@@ -6,4 +6,3 @@ export { default as MultipleSelect } from './MultipleSelect'
 export { default as ProxyUrlInput } from './ProxyUrlInput'
 export { default as PruneInput } from './PruneInput'
 export { default as RateLimitInput } from './RateLimitInput'
-export { default as SslOptionsInput } from './SslOptionsInput'
diff --git a/src/views/settings/index.vue b/src/views/settings/index.vue
index e35de85f22cd163723f2242172367ed94c5f013f..a2edc6869ecb15e863234d09bf29795b008b7479 100644
--- a/src/views/settings/index.vue
+++ b/src/views/settings/index.vue
@@ -17,15 +17,9 @@
       <el-tab-pane :label="$t('settings.captcha')" lazy>
         <captcha/>
       </el-tab-pane>
-      <el-tab-pane :label="$t('settings.database')" lazy>
-        <database/>
-      </el-tab-pane>
       <el-tab-pane :label="$t('settings.emojiPacks')" lazy>
         <emoji-packs/>
       </el-tab-pane>
-      <el-tab-pane :label="$t('settings.endpoint')" lazy>
-        <endpoint/>
-      </el-tab-pane>
       <el-tab-pane :label="$t('settings.frontend')" lazy>
         <frontend/>
       </el-tab-pane>
@@ -76,11 +70,11 @@
 </template>
 
 <script>
-import { ActivityPub, Authentication, AutoLinker, Captcha, Database, Endpoint, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Relays, Upload, WebPush } from './components'
+import { ActivityPub, Authentication, AutoLinker, Captcha, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Relays, Upload, WebPush } from './components'
 import EmojiPacks from '../emojiPacks/index'
 
 export default {
-  components: { ActivityPub, Authentication, AutoLinker, Captcha, Database, Endpoint, EmojiPacks, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Relays, Upload, WebPush },
+  components: { ActivityPub, Authentication, AutoLinker, Captcha, EmojiPacks, Esshd, Frontend, Gopher, Http, Instance, JobQueue, Logger, Mailer, MediaProxy, Metadata, Mrf, Other, RateLimiters, Relays, Upload, WebPush },
   data() {
     return {
       activeTab: 'instance'
diff --git a/src/views/settings/styles/main.scss b/src/views/settings/styles/main.scss
index 45491ab84d6364c31fef725d7a5793851c58467c..4ed52dfecad8baad3281a172e7f74d22162eb4e6 100644
--- a/src/views/settings/styles/main.scss
+++ b/src/views/settings/styles/main.scss
@@ -23,6 +23,9 @@
   .el-form-item {
     margin-right: 30px;
   }
+  .el-form-item .rate-limit {
+    margin-right: 0;
+  }
   .center-label label {
     text-align: center;
   }
@@ -95,8 +98,8 @@
     margin-left: 10px;
   }
   .limit-input {
-    width: 48%;
-    margin: 0 0 5px 8px
+    width: 47%;
+    margin: 0 0 5px 1%
   }
   .line {
     width: 100%;
@@ -130,6 +133,15 @@
     margin-left: 8px;
     margin-right: 10px
   }
+  .replacement-input {
+    width: 80%;
+    margin-left: 8px;
+    margin-right: 10px
+  }
+  .scale-input {
+    width: 47%;
+    margin: 0 1% 5px 0
+  }
   .setting-input {
     display: flex;
     margin-bottom: 10px;
@@ -137,22 +149,13 @@
   .single-input {
     margin-right: 10px
   }
-  .scale-input {
-    width: 48%;
-    margin: 0 8px 5px 0
-  }
-  .replacement-input {
-    width: 80%;
-    margin-left: 8px;
-    margin-right: 10px
+  .ssl-tls-opts {
+    margin: 36px 0 0 0;
   }
   .text {
     line-height: 20px;
     margin-right: 15px
   }
-  .ssl-tls-opts {
-    margin: 36px 0 0 0;
-  }
   .upload-container {
     display: flex;
     align-items: baseline;
diff --git a/test/modules/normalizers/wrapUpdatedSettings.test.js b/test/modules/normalizers/wrapUpdatedSettings.test.js
index 9f815760e984e387c7f636107b48569495ad7c38..6b360f85c8b90f495c2e943b4ca10346a354bc64 100644
--- a/test/modules/normalizers/wrapUpdatedSettings.test.js
+++ b/test/modules/normalizers/wrapUpdatedSettings.test.js
@@ -31,7 +31,8 @@ describe('Wrap settings', () => {
     const result = wrapUpdatedSettings(':mime', settings, {})
     const expectedResult = [{
       group: ':mime',
-      key: ':types', value: {
+      key: ':types',
+      value: {
         'application/ld+json': ['activity+json'],
         'application/xml': ['xml'],
         'application/xrd+xml': ['xrd+xml']
@@ -39,4 +40,335 @@ describe('Wrap settings', () => {
     }]
     expect(_.isEqual(result, expectedResult)).toBeTruthy()
   })
+
+  it('wraps :mascots setting in group :assets', () => {
+    const settings = { ':assets': { ':mascots': [['keyword', 'map'], {
+      ':pleroma_fox_tan_shy': ['', { ':mime_type': 'image/png', ':url': '/images/pleroma-fox-tan-shy.png' }],
+      ':pleroma_fox_tan': ['', { ':mime_type': 'image/png', ':url': '/images/pleroma-fox-tan-smol.png' }],
+    }]}}
+    const state = { ':pleroma': { ':assets': {}}}
+    const result = wrapUpdatedSettings(':pleroma', settings, state)
+    const expectedResult = [{
+      group: ':pleroma',
+      key: ':assets',
+      value: [{ tuple: [':mascots', [
+        { tuple: [':pleroma_fox_tan_shy', { ':mime_type': 'image/png', ':url': '/images/pleroma-fox-tan-shy.png'}] },
+        { tuple: [':pleroma_fox_tan', { ':mime_type': 'image/png', ':url': '/images/pleroma-fox-tan-smol.png'}] }
+      ]]}]
+    }]
+    expect(_.isEqual(result, expectedResult)).toBeTruthy()
+  })
+
+  it('wraps settings with type keyword', () => {
+    const settings1 = { 'Pleroma.Upload': { ':proxy_opts':
+      [ 'keyword', {
+        ':redirect_on_failure': ['boolean', true],
+        ':http': ['keyword', { ':proxy_url': [['string', 'tuple'], 'localhost:3090' ]}]
+      }]
+    }}
+    const state1 = { ':pleroma': { 'Pleroma.Upload': {}}}
+    const result1 = wrapUpdatedSettings(':pleroma', settings1, state1)
+    const expectedResult1 = [{
+      group: ':pleroma',
+      key: 'Pleroma.Upload',
+      value: [{ tuple: [':proxy_opts', [
+        { tuple: [':redirect_on_failure', true] },
+        { tuple: [':http', [{ tuple: [':proxy_url', 'localhost:3090'] }]] }
+      ]]}]
+    }]
+
+    const settings2 = { ':media_proxy': { ':proxy_opts':
+      ['keyword', {
+        ':max_body_length': ['integer', 26210000],
+        ':http': ['keyword', {
+          ':proxy_url': [['string', 'tuple'], [':socks5', '127.0.0.1', '9020']],
+          ':adapter': ['keyword', {
+            ':ssl_options': ['keyword', {
+              ':versions': [['list', 'atom'], [':tlsv1', ':tlsv1.1']]
+            }]
+          }]
+        }]
+      }]
+    }}
+    const state2 = { ':pleroma': { ':media_proxy': {}}}
+    const result2 = wrapUpdatedSettings(':pleroma', settings2, state2)
+    const expectedResult2 = [{
+      group: ':pleroma',
+      key: ':media_proxy',
+      value: [{ tuple: [':proxy_opts', [
+        { tuple: [':max_body_length', 26210000] },
+        { tuple: [':http',
+          [{ tuple: [ ':proxy_url', { tuple: [ ':socks5', '127.0.0.1', '9020' ] }]},
+          { tuple: [':adapter',
+            [{ tuple: [':ssl_options', [{ tuple: [':versions', [':tlsv1', ':tlsv1.1']]}]]}]
+          ]}]
+        ]}
+      ]]}]
+    }]
+
+    expect(_.isEqual(result1, expectedResult1)).toBeTruthy()
+    expect(_.isEqual(result2, expectedResult2)).toBeTruthy()
+  })
+
+  it('wraps settings that includes keyword in type', () => {
+    const settings1 = { 'Oban': { ':queues': [
+      ['keyword', 'integer'],
+      { ':activity_expiration': ['integer', 15],
+        ':background': ['integer', 10],
+        ':federator_incoming': ['integer', 30]}
+    ]}}
+    const state1 = { ':pleroma': { 'Oban': {}}}
+    const result1 = wrapUpdatedSettings(':pleroma', settings1, state1)
+    const expectedResult1 = [{
+      group: ':pleroma',
+      key: 'Oban',
+      value: [{ tuple: [':queues', [
+        { tuple: [':activity_expiration', 15] },
+        { tuple: [':background', 10] },
+        { tuple: [':federator_incoming', 30]}
+      ]]}]
+    }]
+
+    const settings2 = { ':emoji': { ':groups': [
+      ['keyword', 'string', ['list', 'string']],
+      { ':custom': [['list'], ['/emoji/*.png', '/emoji/**/*.png']],
+        ':another_group': ['list', ['/custom_emoji/*.png']]}
+    ]}}
+    const state2 = { ':pleroma': { ':emoji': {}}}
+    const result2 = wrapUpdatedSettings(':pleroma', settings2, state2)
+    const expectedResult2 = [{
+      group: ':pleroma',
+      key: ':emoji',
+      value: [{ tuple: [':groups', [
+        { tuple: [':custom', ['/emoji/*.png', '/emoji/**/*.png']]},
+        { tuple: [':another_group', ['/custom_emoji/*.png']]}
+      ]]}]
+    }]
+
+    expect(_.isEqual(result1, expectedResult1)).toBeTruthy()
+    expect(_.isEqual(result2, expectedResult2)).toBeTruthy()
+  })
+
+  it('wraps :replace setting', () => {
+    const settings = { ':mrf_keyword': { ':replace': [
+      [['tuple', 'string', 'string'], ['tuple', 'regex', 'string']],
+      { 'pattern': ['list', 'replacement'],
+        '/\w+/': ['list', 'test_replacement']}
+    ]}}
+    const state = { ':pleroma': { ':mrf_keyword': {}}}
+    const result = wrapUpdatedSettings(':pleroma', settings, state)
+    const expectedResult = [{
+      group: ':pleroma',
+      key: ':mrf_keyword',
+      value: [{ tuple: [':replace', [
+        { tuple: ['pattern', 'replacement'] },
+        { tuple: ['/\w+/', 'test_replacement'] }
+      ]]}]
+    }]
+
+    expect(_.isEqual(result, expectedResult)).toBeTruthy()
+  })
+
+  it('wraps settings with type atom', () => {
+    const settings = {
+      ':ldap': { ':sslopts': [ 'keyword', { ':verify': ['atom', 'verify_peer']}]},
+      ':assets': { ':default_mascot': ['atom', 'pleroma_fox_tan_test']}
+    }
+    const state = { ':pleroma': { ':sslopts': {}, ':assets': {}}}
+    const result = wrapUpdatedSettings(':pleroma', settings, state)
+    const expectedResult = [{
+      group: ':pleroma',
+      key: ':ldap',
+      value: [{ tuple: [':sslopts', [{tuple: [':verify', ':verify_peer']}]]}]
+    }, {
+      group: ':pleroma',
+      key: ':assets',
+      value: [{ tuple: [':default_mascot', ':pleroma_fox_tan_test']}]
+    }]
+
+    expect(_.isEqual(result, expectedResult)).toBeTruthy()
+  })
+
+  it('wraps settings with type string and tuple', () => {
+    const settings1 = { ':media_proxy': { ':proxy_opts':
+      ['keyword', {
+        ':http': ['keyword', { ':proxy_url': [['string', 'tuple'], 'localhost:9020']}]
+      }]
+    }}
+    const state1 = { ':pleroma': { ':media_proxy': {}}}
+    const result1 = wrapUpdatedSettings(':pleroma', settings1, state1)
+    const expectedResult1 = [{
+      group: ':pleroma',
+      key: ':media_proxy',
+      value: [{ tuple: [':proxy_opts', [
+        { tuple: [':http',
+          [{ tuple: [':proxy_url', 'localhost:9020'] }]
+        ]}
+      ]]}]
+    }]
+
+    const settings2 = { ':media_proxy': { ':proxy_opts':
+      ['keyword', {
+        ':http': ['keyword', { ':proxy_url': [['string', 'tuple'], [':socks5', '127.0.0.1', '9020']]}]
+      }]
+    }}
+    const state2 = { ':pleroma': { ':media_proxy': {}}}
+    const result2 = wrapUpdatedSettings(':pleroma', settings2, state2)
+    const expectedResult2 = [{
+      group: ':pleroma',
+      key: ':media_proxy',
+      value: [{ tuple: [':proxy_opts', [
+        { tuple: [':http',
+          [{ tuple: [ ':proxy_url', { tuple: [':socks5', '127.0.0.1', '9020'] }]}]
+        ]}
+      ]]}]
+    }]
+
+    expect(_.isEqual(result1, expectedResult1)).toBeTruthy()
+    expect(_.isEqual(result2, expectedResult2)).toBeTruthy()
+  })
+
+  it('wraps settings with type atom and tuple', () => {
+    const settings1 = { 'Oban': { ':prune': [['atom', 'tuple'], ':disabled']}}
+    const state1 = { ':pleroma': { 'Oban': {}}}
+    const result1 = wrapUpdatedSettings(':pleroma', settings1, state1)
+    const expectedResult1 = [{
+      group: ':pleroma',
+      key: 'Oban',
+      value: [{ tuple: [':prune', ':disabled']}]
+    }]
+
+    const settings2 = { 'Oban': { ':prune':
+      [['atom', 'tuple'], [':maxlen', 1500]]
+    }}
+    const state2 = { ':pleroma': { 'Oban': {}}}
+    const result2 = wrapUpdatedSettings(':pleroma', settings2, state2)
+    const expectedResult2 = [{
+      group: ':pleroma',
+      key: 'Oban',
+      value: [{ tuple: [':prune', {tuple: [':maxlen', 1500]}]}]
+    }]
+
+    expect(_.isEqual(result1, expectedResult1)).toBeTruthy()
+    expect(_.isEqual(result2, expectedResult2)).toBeTruthy()
+  })
+
+  it('wraps settings with type map', () => {
+    const settings1 = { ':instance': { ':poll_limits': ['map', { ':min_expiration': ['integer', 100] }]}}
+    const state1 = { ':pleroma': { ':instance': { ':poll_limits': {
+      ':max_expiration': 31536000,
+      ':max_option_chars': 200,
+      ':max_options': 20,
+      ':min_expiration': 100
+    }}}}
+    const result1 = wrapUpdatedSettings(':pleroma', settings1, state1)
+    const expectedResult1 = [{
+      group: ':pleroma',
+      key: ':instance',
+      value: [{ tuple: [':poll_limits', {
+        ':max_expiration': 31536000,
+        ':max_option_chars': 200,
+        ':max_options': 20,
+        ':min_expiration': 100
+      }]}]
+    }]
+
+    const settings2 = { ':email_notifications': { ':digest': ['map', {
+      ':active': ['boolean', true],
+      ':schedule': ['string', '0 0  0'],
+      ':inactivity_threshold': ['integer', 10]
+    }]}}
+    const state2 = { ':pleroma': { ':email_notifications': { ':digest': {
+      ':active': true,
+      ':inactivity_threshold': 10,
+      ':interval': 7,
+      ':schedule': '0 0  0'
+    }}}}
+    const result2 = wrapUpdatedSettings(':pleroma', settings2, state2)
+    const expectedResult2 = [{
+      group: ':pleroma',
+      key: ':email_notifications',
+      value: [{ tuple: [':digest', {
+        ':active': true,
+        ':inactivity_threshold': 10,
+        ':interval': 7,
+        ':schedule': '0 0  0'
+      }]}]
+    }]
+
+    const settings3 = { ':mrf_subchain': { ':match_actor': ['map', {
+      '~r/https:\/\/example.com/s': ['Elixir.Pleroma.Web.ActivityPub.MRF.DropPolicy'],
+      '~r/https:\/\/test.com': ['Elixir.Pleroma.Web.ActivityPub.MRF.TestPolicy']
+    }]}}
+    const state3 = { ':pleroma': { ':mrf_subchain': { ':match_actor': [
+      { '~r/https:\/\/example.com/s': ['Elixir.Pleroma.Web.ActivityPub.MRF.DropPolicy'] },
+      { '~r/https:\/\/test.com': ['Elixir.Pleroma.Web.ActivityPub.MRF.TestPolicy'] }
+    ]
+    }}}
+    const result3 = wrapUpdatedSettings(':pleroma', settings3, state3)
+    const expectedResult3 = [{
+      group: ':pleroma',
+      key: ':mrf_subchain',
+      value: [{ tuple: [':match_actor', {
+        '~r/https:\/\/example.com/s': ['Elixir.Pleroma.Web.ActivityPub.MRF.DropPolicy'],
+        '~r/https:\/\/test.com': ['Elixir.Pleroma.Web.ActivityPub.MRF.TestPolicy']
+      }]}]
+    }]
+
+    expect(_.isEqual(result1, expectedResult1)).toBeTruthy()
+    expect(_.isEqual(result2, expectedResult2)).toBeTruthy()
+    expect(_.isEqual(result3, expectedResult3)).toBeTruthy()
+  })
+
+  it('wraps IP setting', () => {
+    const settings = { ':gopher': { ':ip': ['tuple', '127.0.0.1']}}
+    const state = { ':pleroma': { ':gopher': {}}}
+    const result = wrapUpdatedSettings(':pleroma', settings, state)
+    const expectedResult = [{
+      group: ':pleroma',
+      key: ':gopher',
+      value: [{ tuple: [':ip', { tuple: [127, 0, 0, 1] }]}]
+    }]
+
+    expect(_.isEqual(result, expectedResult)).toBeTruthy()
+  })
+
+  it('wraps args setting in Pleroma.Upload.Filter.Mogrify group', () => {
+    const settings = { 'Pleroma.Upload.Filter.Mogrify': { ':args': [
+      ['string', ['list', 'string'], ['list', 'tuple']],
+      ['strip', 'implode']
+    ]}}
+    const state = { ':pleroma': { 'Pleroma.Upload.Filter.Mogrify': {}}}
+    const result = wrapUpdatedSettings(':pleroma', settings, state)
+    const expectedResult = [{
+      group: ':pleroma',
+      key: 'Pleroma.Upload.Filter.Mogrify',
+      value: [{tuple: [':args', ['strip', {tuple: ['implode', '1']}]]}]
+    }]
+
+    expect(_.isEqual(result, expectedResult)).toBeTruthy()
+  })
+
+  it('wraps regular settings', () => {
+    const settings = { ':http_security': {
+      ':report_uri': ["string", "https://test.com"],
+      ':ct_max_age': ["integer", 150000],
+      ':sts': ["boolean", true],
+      ':methods': [["list", "string"], ["POST", "PUT", "PATCH"]]
+    }}
+    const state = { ':pleroma': { ':http_security': {}}}
+    const result = wrapUpdatedSettings(':pleroma', settings, state)
+    const expectedResult = [{
+      group: ':pleroma',
+      key: ':http_security',
+      value: [
+        { tuple: [":report_uri", "https://test.com"] },
+        { tuple: [":ct_max_age", 150000] },
+        { tuple: [":sts", true] },
+        { tuple: [":methods", ["POST", "PUT", "PATCH"]] }
+      ]
+    }]
+
+    expect(_.isEqual(result, expectedResult)).toBeTruthy()
+  })
 })
diff --git a/yarn.lock b/yarn.lock
index 6f1bcc6ec6e2b396d6da6653643cc9dc6f451d14..d75e92b1ca3fb3cb6e4aa79aae958494c000164e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1868,11 +1868,6 @@ brace-expansion@^1.1.7:
     balanced-match "^1.0.0"
     concat-map "0.0.1"
 
-brace@^0.11.0:
-  version "0.11.1"
-  resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58"
-  integrity sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg=
-
 braces@^2.2.2, braces@^2.3.1, braces@^2.3.2:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
@@ -10207,13 +10202,6 @@ vue-template-es2015-compiler@^1.9.0:
   resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
   integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
 
-vue2-ace-editor@^0.0.13:
-  version "0.0.13"
-  resolved "https://registry.yarnpkg.com/vue2-ace-editor/-/vue2-ace-editor-0.0.13.tgz#5528998ce2c13e8ed3a294f714298199fd107dc2"
-  integrity sha512-uQICyvJzYNix16xeYjNAINuNUQhPbqMR7UQsJeI+ycpEd2otsiNNU73jcZqHkpjuz0uaHDHnrpzQuI/RApsKXA==
-  dependencies:
-    brace "^0.11.0"
-
 vue@^2.6.8:
   version "2.6.8"
   resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.8.tgz#f21cbc536bfc14f7d1d792a137bb12f69e60ea91"