diff --git a/mod/admin.php b/mod/admin.php
index 9b8a054c4..50244901d 100644
--- a/mod/admin.php
+++ b/mod/admin.php
@@ -19,6 +19,7 @@ use Friendica\Core\System;
 use Friendica\Core\Theme;
 use Friendica\Core\Update;
 use Friendica\Core\Worker;
+use Friendica\Core\StorageManager;
 use Friendica\Database\DBA;
 use Friendica\Database\DBStructure;
 use Friendica\Model\Contact;
@@ -1170,6 +1171,39 @@ function admin_page_site_post(App $a)
 	$relay_server_tags = (!empty($_POST['relay_server_tags']) ? Strings::escapeTags(trim($_POST['relay_server_tags']))  : '');
 	$relay_user_tags   = !empty($_POST['relay_user_tags']);
 	$active_panel      = (!empty($_POST['active_panel'])      ? "#" . Strings::escapeTags(trim($_POST['active_panel'])) : '');
+	
+	$storagebackend    = Strings::escapeTags(trim(defaults($_POST, 'storagebackend', '')));
+	StorageManager::setBackend($storagebackend);
+	
+	// save storage backend form
+	$storage_opts = $storagebackend::getOptions();
+	$storage_form_prefix=preg_replace('|[^a-zA-Z0-9]|' ,'', $storagebackend);
+	$storage_opts_data = [];
+	foreach($storage_opts as $name => $info) {
+		$fieldname = $storage_form_prefix . '_' . $name;
+		switch ($info[0]) { // type
+			case 'checkbox':
+			case 'yesno':
+				$value = !empty($_POST[$fieldname]);
+				break;
+			default:
+				$value = defaults($_POST, $fieldname, '');
+		}
+		$storage_opts_data[$name] = $value;
+	}
+	unset($name);
+	unset($info);
+	
+	$storage_form_errors = $storagebackend::saveOptions($storage_opts_data);
+	if (count($storage_form_errors)) {
+		foreach($storage_form_errors as $name => $err) {
+			notice('Storage backend, ' . $storage_opts[$name][1] . ': ' . $err);
+		}
+		$a->internalRedirect('admin/site' . $active_panel);
+	}
+	
+
+	
 
 	// Has the directory url changed? If yes, then resubmit the existing profiles there
 	if ($global_directory != Config::get('system', 'directory') && ($global_directory != '')) {
@@ -1482,6 +1516,31 @@ function admin_page_site(App $a)
 		$optimize_max_tablesize = -1;
 	}
 
+	/* storage backend */
+	$storage_backends = StorageManager::listBackends();
+	$storage_current_backend = StorageManager::getBackend();
+
+	$storage_backends_choices = [
+		'' => L10n::t('None')
+	];
+	foreach($storage_backends as $name=>$class) {
+		$storage_backends_choices[$class] = $name;
+	}
+	unset($storage_backends);
+
+	// build storage config form,
+	$storage_form_prefix=preg_replace('|[^a-zA-Z0-9]|' ,'', $storage_current_backend);
+	
+	$storage_form = [];
+	foreach($storage_current_backend::getOptions() as $name => $info) {
+		$type = $info[0];
+		$info[0] = $storage_form_prefix . '_' . $name;
+		$info['type'] = $type;
+		$info['field'] = 'field_' . $type . '.tpl';
+		$storage_form[$name] = $info;
+	}
+
+
 	$t = Renderer::getMarkupTemplate('admin/site.tpl');
 	return Renderer::replaceMacros($t, [
 		'$title'             => L10n::t('Administration'),
@@ -1515,6 +1574,9 @@ function admin_page_site(App $a)
 		'$force_ssl'        => ['force_ssl', L10n::t("Force SSL"), Config::get('system', 'force_ssl'), L10n::t("Force all Non-SSL requests to SSL - Attention: on some systems it could lead to endless loops.")],
 		'$hide_help'        => ['hide_help', L10n::t("Hide help entry from navigation menu"), Config::get('system', 'hide_help'), L10n::t("Hides the menu entry for the Help pages from the navigation menu. You can still access it calling /help directly.")],
 		'$singleuser'       => ['singleuser', L10n::t("Single user instance"), Config::get('system', 'singleuser', '---'), L10n::t("Make this instance multi-user or single-user for the named user"), $user_names],
+
+		'$storagebackend'   => ['storagebackend', L10n::t("File storage backend"), $storage_current_backend, L10n::t('Backend used to store uploaded files data'), $storage_backends_choices],
+		'$storageform'      => $storage_form,
 		'$maximagesize'     => ['maximagesize', L10n::t("Maximum image size"), Config::get('system', 'maximagesize'), L10n::t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")],
 		'$maximagelength'   => ['maximagelength', L10n::t("Maximum image length"), Config::get('system', 'max_image_length'), L10n::t("Maximum length in pixels of the longest side of uploaded images. Default is -1, which means no limits.")],
 		'$jpegimagequality' => ['jpegimagequality', L10n::t("JPEG image quality"), Config::get('system', 'jpeg_quality'), L10n::t("Uploaded JPEGS will be saved at this quality setting [0-100]. Default is 100, which is full quality.")],
diff --git a/src/Model/Storage/Database.php b/src/Model/Storage/Database.php
index 39a1cb64a..cfd4803c8 100644
--- a/src/Model/Storage/Database.php
+++ b/src/Model/Storage/Database.php
@@ -51,4 +51,8 @@ class Database implements IStorage
 	{
 		return DBA::delete('storage', ['id' => $ref]);
 	}
-}
\ No newline at end of file
+	
+	public static function getOptions() { return []; }
+	
+	public static function saveOptions($data) { return []; }
+}
diff --git a/src/Model/Storage/Filesystem.php b/src/Model/Storage/Filesystem.php
index 1cfb5effe..2f375491b 100644
--- a/src/Model/Storage/Filesystem.php
+++ b/src/Model/Storage/Filesystem.php
@@ -24,11 +24,11 @@ use Friendica\Util\Strings;
 class Filesystem implements IStorage
 {
 	// Default base folder
-	const DEFAULT_BASE_FOLDER = "storage";
+	const DEFAULT_BASE_FOLDER = 'storage';
 
 	private static function getBasePath()
 	{
-		return Config::get("storage", "filesystem_path", self::DEFAULT_BASE_FOLDER);
+		return Config::get('storage', 'filesystem_path', self::DEFAULT_BASE_FOLDER);
 	}
 
 	/**
@@ -43,7 +43,7 @@ class Filesystem implements IStorage
 		$fold2 = substr($ref, 2, 2);
 		$file = substr($ref, 4);
 
-		return "{$base}/{$fold1}/{$fold2}/{$file}";
+		return implode('/', [$base, $fold1, $fold2, $file]);
 	}
 
 
@@ -57,8 +57,8 @@ class Filesystem implements IStorage
 
 		if (!is_dir($path)) {
 			if (!mkdir($path, 0770, true)) {
-				Logger::log("Failed to create dirs {$path}");
-				throw new StorageException(L10n::t("Filesystem storage failed to create '%s'. Check you write permissions.", $path));
+				Logger::log('Failed to create dirs ' . $path);
+				throw new StorageException(L10n::t('Filesystem storage failed to create "%s". Check you write permissions.', $path));
 				killme();
 			}
 		}
@@ -66,13 +66,13 @@ class Filesystem implements IStorage
 		$base = self::getBasePath();
 
 		while ($path !== $base) {
-			if (!is_file($path . "/index.html")) {
-				file_put_contents($path . "/index.html", "");
+			if (!is_file($path . '/index.html')) {
+				file_put_contents($path . '/index.html', '');
 			}
 			$path = dirname($path);
 		}
-		if (!is_file($path . "/index.html")) {
-			file_put_contents($path . "/index.html", "");
+		if (!is_file($path . '/index.html')) {
+			file_put_contents($path . '/index.html', '');
 		}
 	}
 
@@ -80,15 +80,15 @@ class Filesystem implements IStorage
 	{
 		$file = self::pathForRef($ref);
 		if (!is_file($file)) {
-			return "";
+			return '';
 		}
 
 		return file_get_contents($file);
 	}
 
-	public static function put($data, $ref = "")
+	public static function put($data, $ref = '')
 	{
-		if ($ref === "") {
+		if ($ref === '') {
 			$ref = Strings::getRandomHex();
 		}
 		$file = self::pathForRef($ref);
@@ -97,8 +97,8 @@ class Filesystem implements IStorage
 
 		$r = file_put_contents($file, $data);
 		if ($r === FALSE) {
-			Logger::log("Failed to write data to {$file}");
-			throw new StorageException(L10n::t("Filesystem storage failed to save data to '%s'. Check your write permissions", $file));
+			Logger::log('Failed to write data to ' . $file);
+			throw new StorageException(L10n::t('Filesystem storage failed to save data to "%s". Check your write permissions', $file));
 			killme();
 		}
 		return $ref;
@@ -114,4 +114,28 @@ class Filesystem implements IStorage
 		return unlink($file);
 	}
 
+	public static function getOptions()
+	{
+		return [
+			'storagepath' => [
+				'input',
+				L10n::t('Storage base path'),
+				self::getBasePath(),
+				L10n::t('Folder were uploaded files are saved. For maximum security, This should be a path outside web server folder tree')
+			]
+		];
+	}
+	
+	public static function saveOptions($data)
+	{
+		$storagepath = defaults($data, 'storagepath', '');
+		if ($storagepath === '' || !is_dir($storagepath)) {
+			return [
+				'storagepath' => L10n::t('Enter a valid existing folder')
+			];
+		};
+		Config::set('storage', 'filesystem_path', $storagepath);
+		return [];
+	}
+
 }
diff --git a/src/Model/Storage/IStorage.php b/src/Model/Storage/IStorage.php
index deacb8ffc..1b0129e5e 100644
--- a/src/Model/Storage/IStorage.php
+++ b/src/Model/Storage/IStorage.php
@@ -32,4 +32,58 @@ interface IStorage
 	 * @return boolean  True on success
 	 */
 	public static function delete($ref);
+	
+	/**
+	 * @brief Get info about storage options
+	 *
+	 * @return array
+	 *
+	 * This method return an array with informations about storage options
+	 * from which the form presented to the user is build.
+	 *
+	 * The returned array is:
+	 *
+	 *    [
+	 *      'option1name' => [ ..info.. ],
+	 *      'option2name' => [ ..info.. ],
+	 *      ...
+	 *    ]
+	 *
+	 * An empty array can be returned if backend doesn't have any options
+	 *
+	 * The info array for each option MUST be as follows:
+	 *
+	 *    [
+	 *      'type',      // define the field used in form, and the type of data.
+	 *                   // one of 'checkbox', 'combobox', 'custom', 'datetime',
+	 *                   // 'input', 'intcheckbox', 'password', 'radio', 'richtext'
+	 *                   // 'select', 'select_raw', 'textarea', 'yesno'
+	 *
+	 *      'label',     // Translatable label of the field
+	 *      'value',     // Current value
+	 *      'help text', // Translatable description for the field
+	 *      extra data   // Optional. Depends on 'type':
+	 *                   // select: array [ value => label ] of choices
+	 *                   // intcheckbox: value of input element
+	 *                   // select_raw: prebuild html string of < option > tags
+	 *                   // yesno: array [ 'label no', 'label yes']
+	 *    ]
+	 *
+	 * See https://github.com/friendica/friendica/wiki/Quick-Template-Guide
+	 */
+	public static function getOptions();
+	
+	/**
+	 * @brief Validate and save options
+	 *
+	 * @param array  $data  Array [optionname => value] to be saved
+	 *
+	 * @return array  Validation errors: [optionname => error message]
+	 *
+	 * Return array must be empty if no error.
+	 */
+	public static function saveOptions($data);
+	
 }
+
+
diff --git a/view/templates/admin/site.tpl b/view/templates/admin/site.tpl
index a7adfd5a1..98d0dadfb 100644
--- a/view/templates/admin/site.tpl
+++ b/view/templates/admin/site.tpl
@@ -71,6 +71,11 @@
 	<div class="submit"><input type="submit" name="page_site" value="{{$submit}}" /></div>
 
 	<h3>{{$upload}}</h3>
+	{{include file="field_select.tpl" field=$storagebackend}}
+	{{foreach from=$storageform item=$field}}
+		{{include file=$field.field field=$field}}
+	{{/foreach}}
+	<hr>
 	{{include file="field_input.tpl" field=$maximagesize}}
 	{{include file="field_input.tpl" field=$maximagelength}}
 	{{include file="field_input.tpl" field=$jpegimagequality}}
diff --git a/view/theme/frio/templates/admin/site.tpl b/view/theme/frio/templates/admin/site.tpl
index 5d4fd2506..c8ee133a9 100644
--- a/view/theme/frio/templates/admin/site.tpl
+++ b/view/theme/frio/templates/admin/site.tpl
@@ -135,7 +135,11 @@
 					</h4>
 				</div>
 				<div id="admin-settings-upload-collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="admin-settings-upload">
-
+					{{include file="field_select.tpl" field=$storagebackend}}
+					{{foreach from=$storageform item=$field}}
+						{{include file=$field.field field=$field}}
+					{{/foreach}}
+					<hr>
 					{{include file="field_input.tpl" field=$maximagesize}}
 					{{include file="field_input.tpl" field=$maximagelength}}
 					{{include file="field_input.tpl" field=$jpegimagequality}}