diff --git a/actions/syncthing b/actions/syncthing
new file mode 100755
index 000000000..f3a4a3677
--- /dev/null
+++ b/actions/syncthing
@@ -0,0 +1,71 @@
+#!/usr/bin/python3
+# -*- mode: python -*-
+#
+# This file is part of Plinth.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+"""
+Configuration helper for Syncthing.
+"""
+
+import argparse
+import augeas
+import subprocess
+
+from plinth import action_utils
+
+
+def parse_arguments():
+ """Return parsed command line arguments as dictionary."""
+ parser = argparse.ArgumentParser()
+ subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
+
+ subparsers.add_parser('pre-setup', help='Perform pre-setup operations')
+ subparsers.add_parser('setup', help='Setup Syncthing configuration')
+ subparsers.add_parser('enable', help='Enable Syncthing site')
+ subparsers.add_parser('disable', help='Disable Syncthing site')
+
+ return parser.parse_args()
+
+
+def subcommand_setup(_):
+ """Setup Syncthing configuration."""
+ action_utils.service_restart('syncthing')
+ action_utils.webserver_enable('syncthing-plinth')
+
+
+def subcommand_enable(_):
+ """Enable web configuration and reload."""
+ action_utils.service_enable('syncthing')
+ action_utils.webserver_enable('syncthing-plinth')
+
+
+def subcommand_disable(_):
+ """Disable web configuration and reload."""
+ action_utils.webserver_disable('syncthing-plinth')
+ action_utils.service_disable('syncthing')
+
+
+def main():
+ """Parse arguments and perform all duties."""
+ arguments = parse_arguments()
+
+ subcommand = arguments.subcommand.replace('-', '_')
+ subcommand_method = globals()['subcommand_' + subcommand]
+ subcommand_method(arguments)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/data/etc/apache2/conf-available/syncthing-plinth.conf b/data/etc/apache2/conf-available/syncthing-plinth.conf
new file mode 100644
index 000000000..49c43b030
--- /dev/null
+++ b/data/etc/apache2/conf-available/syncthing-plinth.conf
@@ -0,0 +1,5 @@
+ProxyPass /syncthing/ http://localhost:8384/
+
+ ProxyPassReverse http://localhost:8384/
+ Require all granted
+
\ No newline at end of file
diff --git a/data/etc/plinth/modules-enabled/syncthing b/data/etc/plinth/modules-enabled/syncthing
new file mode 100644
index 000000000..284bd0bd2
--- /dev/null
+++ b/data/etc/plinth/modules-enabled/syncthing
@@ -0,0 +1 @@
+plinth.modules.syncthing
\ No newline at end of file
diff --git a/plinth/modules/syncthing/__init__.py b/plinth/modules/syncthing/__init__.py
new file mode 100644
index 000000000..6d89443d3
--- /dev/null
+++ b/plinth/modules/syncthing/__init__.py
@@ -0,0 +1,120 @@
+#
+# This file is part of Plinth.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+"""
+Plinth module to configure Syncthing.
+"""
+
+from django.utils.translation import ugettext_lazy as _
+
+from plinth import actions
+from plinth import action_utils
+from plinth import cfg
+from plinth import frontpage
+from plinth import service as service_module
+
+version = 1
+
+depends = ['apps']
+
+managed_services = ['syncthing']
+
+managed_packages = ['syncthing']
+
+title = _('Personal cloud service\n (Syncthing)')
+
+description = [
+ _('Syncthing is a news feed (RSS/Atom) reader and aggregator, '
+ 'designed to allow reading news from any location, while feeling as '
+ 'close to a real desktop application as possible.'),
+ _('When enabled, Syncthing will be available from '
+ '/syncthing path on the web server.'),
+]
+
+service = None
+
+
+def init():
+ """Intialize the module."""
+ menu = cfg.main_menu.get('apps:index')
+ menu.add_urlname(title, 'glyphicon-envelope', 'syncthing:index')
+
+ global service
+ setup_helper = globals()['setup_helper']
+ if setup_helper.get_state() != 'needs-setup':
+ service = service_module.Service(
+ managed_services[0],
+ title,
+ ports=['http', 'https'],
+ is_external=True,
+ is_enabled=is_enabled,
+ enable=enable,
+ disable=disable)
+
+ if is_enabled():
+ add_shortcut()
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(managed_packages)
+ helper.call('post', actions.superuser_run, 'syncthing', ['setup'])
+ global service
+ if service is None:
+ service = service_module.Service(
+ managed_services[0],
+ title,
+ ports=['http', 'https'],
+ is_external=True,
+ is_enabled=is_enabled,
+ enable=enable,
+ disable=disable)
+ helper.call('post', service.notify_enabled, None, True)
+ helper.call('post', add_shortcut)
+
+
+def add_shortcut():
+ frontpage.add_shortcut(
+ 'syncthing', title, url='/syncthing', login_required=True)
+
+
+def is_enabled():
+ """Return whether the module is enabled."""
+ return (action_utils.service_is_enabled('syncthing') and
+ action_utils.webserver_is_enabled('syncthing-plinth'))
+
+
+def enable():
+ """Enable the module."""
+ actions.superuser_run('syncthing', ['enable'])
+ add_shortcut()
+
+
+def disable():
+ """Enable the module."""
+ actions.superuser_run('syncthing', ['disable'])
+ frontpage.remove_shortcut('syncthing')
+
+
+def diagnose():
+ """Run diagnostics and return the results."""
+ results = []
+
+ results.extend(
+ action_utils.diagnose_url_on_all(
+ 'https://{host}/syncthing', check_certificate=False))
+
+ return results
diff --git a/plinth/modules/syncthing/urls.py b/plinth/modules/syncthing/urls.py
new file mode 100644
index 000000000..49f1ec380
--- /dev/null
+++ b/plinth/modules/syncthing/urls.py
@@ -0,0 +1,34 @@
+#
+# This file is part of Plinth.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+"""
+URLs for the Syncthing module.
+"""
+
+from django.conf.urls import url
+
+from plinth.views import ServiceView
+from plinth.modules import syncthing
+
+urlpatterns = [
+ url(r'^apps/syncthing/$',
+ ServiceView.as_view(
+ service_id=syncthing.managed_services[0],
+ diagnostics_module_name="syncthing",
+ description=syncthing.description,
+ show_status_block=True),
+ name='index'),
+]