diff --git a/contrib/systemd/README.txt b/contrib/systemd/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3225641f49612d41026bfe7fde7baeecf86acc07
--- /dev/null
+++ b/contrib/systemd/README.txt
@@ -0,0 +1,119 @@
+SystemD Socket Activation for Asterisk
+======================================
+
+This folder contains sample unit files which can be used as the basis of a
+socket activated Asterisk deployment.  Socket activation support currently
+extends to the following listeners:
+
+* Asterisk Command-line Interface
+* Asterisk Manager Interface (clear text and TLS)
+* Builtin HTTP / HTTPS server
+
+The primary use case of this feature is to allow Asterisk to be started by
+other services through use of AMI, CLI or REST API.
+
+
+Security
+========
+
+Care must be take if enabling socket activation on any IP:PORT that is not
+protected by a firewall.  Any user that can reach any socket activation
+port can start Asterisk, even if they do not have valid credentials to sign
+into the service in question.  Enabling HTTP socket activation on a system
+which provides SIP over websockets would allow remote users to start Asterisk
+any time the HTTP socket is running.
+
+This functionality bypasses the normal restriction where only 'root' can start
+a service.  Enabling AMI socket activation allows any user on the local server
+to start Asterisk by running 'telnet localhost 5038'.
+
+CLI activation is secured by the combination of SocketUser, SocketGroup and
+SocketMode settings in the systemd socket.  Only local users with access will
+be able to start asterisk by using CLI.
+
+
+Separate .socket units or a single unit
+=======================================
+
+Asterisk is a complex system with many components which can be enabled or
+disabled individually.  Using socket activation requires deciding to use
+a single socket file or multiple separate socket files.
+
+The remainder of this README assumes separate socket units are used for each
+listener.
+
+
+Service and Socket files
+========================
+
+All .socket and .service examples in this folder use "reasonable" default
+paths for Linux.  Depending on your distribution and ./configure options
+you may need to modify these before installing.  The files are meant to
+be examples rather than files to be blindly installed.
+
+
+Installing and enabling socket units
+====================================
+
+Modify socket files as desired.  Install them to a location where systemd
+will find them.  pkg-config can be used to determine an appropriate location.
+
+For socket files to be managed directly by the local administrator:
+    pkg-config systemd --variable systemdsystemconfdir
+
+For socket files to be deployed by package manager:
+    pkg-config systemd --variable systemdsystemunitdir
+
+
+After installing socket files you must run 'systemctl daemon-reload' for
+systemd to read the added/modified units.  After this you can enable the
+desired sockets, for example to enable AMI:
+    systemctl enable asterisk-ami.socket
+
+
+Socket Selection
+================
+
+Asterisk configuration is unchanged by use of socket activation.  When a
+component that supports socket activation starts a listener in Asterisk,
+any sockets provided by systemd are iterated.  The systemd socket is used
+when the bound address configured by Asterisk is an exact match with the
+address given by the ListenStream setting in the systemd socket.
+
+
+Command-line Interface
+======================
+
+Symbolic links do not appear to be resolved when checking the CLI listener.
+This may be of concern since /var/run is often a symbolic link to /run. Both
+Asterisk and systemd must use /var/run, or both must use /run.  Mismatching
+will result in service startup failure.
+
+When socket activation is used for Asterisk CLI some asterisk.conf options
+are ignored.  The following options from the [files] section are ignored
+and must instead be set by the systemd socket file.
+* astctlowner - use SocketUser
+* astctlgroup - use SocketGroup
+* astctlpermissions - use SocketMode
+
+See asterisk-cli.socket for an example of these settings.
+
+
+Stopping Asterisk
+=================
+
+Some existing asterisk.service files use CLI 'core stop now' for the ExecStop
+command.  It is not recommended to use CLI to stop Asterisk on systems where
+CLI socket activation is enabled.  If Asterisk fails to start systemd still
+tries running the ExecStop command.  This can result in an loop where ExecStop
+causes CLI socket activation to start Asterisk again.  A better way to deal
+with shutdown is to use Type=notify and do not specify an ExecStop command.
+See the example asterisk.service.
+
+
+Unused Sockets
+==============
+
+Asterisk makes no attempt to check for sockets provided by systemd that are not
+used.  It is the users responsibility to only provide sockets which Asterisk is
+configured to use.
diff --git a/contrib/systemd/asterisk-ami.socket b/contrib/systemd/asterisk-ami.socket
new file mode 100644
index 0000000000000000000000000000000000000000..1fd45e4cb871a68f956f3dec2e84ad61bf97837b
--- /dev/null
+++ b/contrib/systemd/asterisk-ami.socket
@@ -0,0 +1,10 @@
+[Unit]
+Description=Asterisk Manager Interface Socket
+
+[Socket]
+Service=asterisk.service
+ListenStream=0.0.0.0:5038
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk-amis.socket b/contrib/systemd/asterisk-amis.socket
new file mode 100644
index 0000000000000000000000000000000000000000..c17cee3e268ad2ea0ba4b7fa4b2ca66f64f3a342
--- /dev/null
+++ b/contrib/systemd/asterisk-amis.socket
@@ -0,0 +1,10 @@
+[Unit]
+Description=Asterisk Manager Interface TLS Socket
+
+[Socket]
+Service=asterisk.service
+ListenStream=0.0.0.0:5039
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk-cli.socket b/contrib/systemd/asterisk-cli.socket
new file mode 100644
index 0000000000000000000000000000000000000000..9161a7be466d355e49bfadee84855269799dcb30
--- /dev/null
+++ b/contrib/systemd/asterisk-cli.socket
@@ -0,0 +1,13 @@
+[Unit]
+Description=Asterisk Command-line Interface Socket
+
+[Socket]
+Service=asterisk.service
+ListenStream=/var/run/asterisk/asterisk.ctl
+SocketUser=asterisk
+SocketGroup=asterisk
+SocketMode=0660
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk-http.socket b/contrib/systemd/asterisk-http.socket
new file mode 100644
index 0000000000000000000000000000000000000000..e6862b5b9228e7a49745cb1360b61342a49e5104
--- /dev/null
+++ b/contrib/systemd/asterisk-http.socket
@@ -0,0 +1,11 @@
+[Unit]
+Description=Asterisk HTTP Socket
+
+[Socket]
+Service=asterisk.service
+FreeBind=true
+ListenStream=127.0.0.1:8088
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk-https.socket b/contrib/systemd/asterisk-https.socket
new file mode 100644
index 0000000000000000000000000000000000000000..d9240dd910633ac89086e6427e4a9e323f25d486
--- /dev/null
+++ b/contrib/systemd/asterisk-https.socket
@@ -0,0 +1,11 @@
+[Unit]
+Description=Asterisk HTTPS Socket
+
+[Socket]
+Service=asterisk.service
+FreeBind=true
+ListenStream=127.0.0.1:8089
+
+[Install]
+WantedBy=sockets.target
+RequiredBy=asterisk.service
diff --git a/contrib/systemd/asterisk.service b/contrib/systemd/asterisk.service
new file mode 100644
index 0000000000000000000000000000000000000000..c3d46483c731a7a17f12be5baf8d21cc3bc27311
--- /dev/null
+++ b/contrib/systemd/asterisk.service
@@ -0,0 +1,27 @@
+[Unit]
+Description=Asterisk PBX and telephony daemon.
+After=network.target
+
+[Service]
+Type=notify
+Environment=HOME=/var/lib/asterisk
+WorkingDirectory=/var/lib/asterisk
+User=asterisk
+Group=asterisk
+ExecStart=/usr/sbin/asterisk -mqf -C /etc/asterisk/asterisk.conf
+ExecReload=/usr/sbin/asterisk -rx 'core reload'
+
+#Nice=0
+#UMask=0002
+LimitCORE=infinity
+#LimitNOFILE=
+Restart=always
+RestartSec=4
+
+# Prevent duplication of logs with color codes to /var/log/messages
+StandardOutput=null
+
+PrivateTmp=true
+
+[Install]
+WantedBy=multi-user.target
diff --git a/contrib/systemd/asterisk.socket b/contrib/systemd/asterisk.socket
new file mode 100644
index 0000000000000000000000000000000000000000..afdca0df78a635229da8e1b1ca75429cc75ecfef
--- /dev/null
+++ b/contrib/systemd/asterisk.socket
@@ -0,0 +1,26 @@
+[Unit]
+Description=Asterisk Sockets
+
+[Socket]
+FreeBind=true
+SocketUser=asterisk
+SocketGroup=asterisk
+SocketMode=0660
+
+# CLI
+ListenStream=/var/run/asterisk/asterisk.ctl
+# AMI
+ListenStream=0.0.0.0:5038
+# AMIS
+ListenStream=0.0.0.0:5039
+# HTTP
+ListenStream=127.0.0.1:8088
+# HTTPS
+ListenStream=127.0.0.1:8089
+# chan_sip TCP
+ListenStream=0.0.0.0:5060
+# chan_sip TLS
+ListenStream=0.0.0.0:5061
+
+[Install]
+WantedBy=sockets.target
diff --git a/include/asterisk/io.h b/include/asterisk/io.h
index 6ee8450bd7007d4a05f2b14c0b0cbb6d043fdf07..f103cf556bab7dcf999475ebfcfecebebbc0c161 100644
--- a/include/asterisk/io.h
+++ b/include/asterisk/io.h
@@ -24,6 +24,7 @@
 #define _ASTERISK_IO_H
 
 #include "asterisk/poll-compat.h"
+#include "asterisk/netsock2.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -148,6 +149,29 @@ int ast_get_termcols(int fd);
  */
 int ast_sd_notify(const char *state);
 
+/*!
+ * \brief Find a listening file descriptor provided by socket activation.
+ * \param type SOCK_STREAM or SOCK_DGRAM
+ * \param addr The socket address of the bound listener.
+ * \retval <0 No match.
+ * \retval >0 File Descriptor matching sockaddr.
+ *
+ * \note This function returns -1 if systemd's development headers were not
+ * detected on the system.
+ */
+int ast_sd_get_fd(int type, const struct ast_sockaddr *addr);
+
+/*!
+ * \brief Find a listening AF_LOCAL file descriptor provided by socket activation.
+ * \param type SOCK_STREAM or SOCK_DGRAM
+ * \param path The path of the listener.
+ * \retval <0 No match.
+ * \retval >0 File Descriptor matching path.
+ *
+ * \note This function returns -1 if systemd's development headers were not
+ * detected on the system.
+ */
+int ast_sd_get_fd_un(int type, const char *path);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }
diff --git a/include/asterisk/netsock2.h b/include/asterisk/netsock2.h
index 3ede99087788a242e0c33c8cdfe02e51e821a913..6c0dd633b03c140f3e2d1617c441131a427ba841 100644
--- a/include/asterisk/netsock2.h
+++ b/include/asterisk/netsock2.h
@@ -128,6 +128,22 @@ static inline void ast_sockaddr_setnull(struct ast_sockaddr *addr)
 	addr->len = 0;
 }
 
+/*!
+ * \brief
+ * Copies the data from a sockaddr to an ast_sockaddr
+ *
+ * \param dst The destination ast_sockaddr
+ * \param src The source sockaddr
+ * \param len Length of the value stored in sockaddr
+ * \retval void
+ */
+static inline void ast_sockaddr_copy_sockaddr(struct ast_sockaddr *dst,
+		struct sockaddr *src, socklen_t len)
+{
+	memcpy(dst, src, len);
+	dst->len = len;
+}
+
 /*!
  * \since 1.8
  *
diff --git a/main/asterisk.c b/main/asterisk.c
index 16313ea685312f017cba277d09f89c835e45bde3..4424022c94a5e55830e52352885a9d85301afdae 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -350,6 +350,7 @@ struct ast_eid ast_eid_default;
 char record_cache_dir[AST_CACHE_DIR_LEN] = DEFAULT_TMP_DIR;
 
 static int ast_socket = -1;		/*!< UNIX Socket for allowing remote control */
+static int ast_socket_is_sd = 0; /*!< Is socket activation responsible for ast_socket? */
 static int ast_consock = -1;		/*!< UNIX Socket for controlling another asterisk */
 pid_t ast_mainpid;
 struct console {
@@ -1576,8 +1577,16 @@ static int ast_makesocket(void)
 	uid_t uid = -1;
 	gid_t gid = -1;
 
-	for (x = 0; x < AST_MAX_CONNECTS; x++)
+	for (x = 0; x < AST_MAX_CONNECTS; x++) {
 		consoles[x].fd = -1;
+	}
+
+	if (ast_socket_is_sd) {
+		ast_socket = ast_sd_get_fd_un(SOCK_STREAM, ast_config_AST_SOCKET);
+
+		goto start_lthread;
+	}
+
 	unlink(ast_config_AST_SOCKET);
 	ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
 	if (ast_socket < 0) {
@@ -1602,12 +1611,19 @@ static int ast_makesocket(void)
 		return -1;
 	}
 
+start_lthread:
 	if (ast_pthread_create_background(&lthread, NULL, listener, NULL)) {
 		ast_log(LOG_WARNING, "Unable to create listener thread.\n");
 		close(ast_socket);
 		return -1;
 	}
 
+	if (ast_socket_is_sd) {
+		/* owner/group/permissions are set by systemd, we might not even have access
+		 * to socket file so leave it alone */
+		return 0;
+	}
+
 	if (!ast_strlen_zero(ast_config_AST_CTL_OWNER)) {
 		struct passwd *pw;
 		if ((pw = getpwnam(ast_config_AST_CTL_OWNER)) == NULL)
@@ -2075,7 +2091,9 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart)
 		pthread_cancel(lthread);
 		close(ast_socket);
 		ast_socket = -1;
-		unlink(ast_config_AST_SOCKET);
+		if (!ast_socket_is_sd) {
+			unlink(ast_config_AST_SOCKET);
+		}
 		pthread_kill(lthread, SIGURG);
 		pthread_join(lthread, NULL);
 	}
@@ -4317,7 +4335,12 @@ int main(int argc, char *argv[])
 	/* Initial value of the maximum active system verbosity level. */
 	ast_verb_sys_level = option_verbose;
 
-	if (ast_tryconnect()) {
+	if (ast_sd_get_fd_un(SOCK_STREAM, ast_config_AST_SOCKET) > 0) {
+		ast_socket_is_sd = 1;
+	}
+
+	/* DO NOT perform check for existing daemon if systemd has CLI socket activation */
+	if (!ast_socket_is_sd && ast_tryconnect()) {
 		/* One is already running */
 		if (ast_opt_remote) {
 			multi_thread_safe = 1;
diff --git a/main/io.c b/main/io.c
index b063c22396c481b81d2bc8130d7fd282e51a4a0d..ed455df977fceee34fc6ebcc76ee41844df7adc1 100644
--- a/main/io.c
+++ b/main/io.c
@@ -36,6 +36,10 @@
 #include "asterisk/utils.h"
 #ifdef HAVE_SYSTEMD
 #include <systemd/sd-daemon.h>
+
+#ifndef SD_LISTEN_FDS_START
+#define SD_LISTEN_FDS_START 3
+#endif
 #endif
 
 #ifdef DEBUG_IO
@@ -392,3 +396,73 @@ int ast_sd_notify(const char *state) {
 	return 0;
 #endif
 }
+
+/*!
+ * \internal \brief Check the type and sockaddr of a file descriptor.
+ * \param fd File Descriptor to check.
+ * \param type SOCK_STREAM or SOCK_DGRAM
+ * \param addr The socket address to match.
+ * \retval 0 if matching
+ * \retval -1 if not matching
+ */
+#ifdef HAVE_SYSTEMD
+static int ast_sd_is_socket_sockaddr(int fd, int type, const struct ast_sockaddr* addr)
+{
+	int canretry = 1;
+	struct ast_sockaddr fd_addr;
+	struct sockaddr ss;
+	socklen_t ss_len;
+
+	if (sd_is_socket(fd, AF_UNSPEC, type, 1) <= 0) {
+		return -1;
+	}
+
+doretry:
+	if (getsockname(fd, &ss, &ss_len) != 0) {
+		return -1;
+	}
+
+	if (ss.sa_family == AF_UNSPEC && canretry) {
+		/* An unknown bug can cause silent failure from
+		 * the first call to getsockname. */
+		canretry = 0;
+		goto doretry;
+	}
+
+	ast_sockaddr_copy_sockaddr(&fd_addr, &ss, ss_len);
+
+	return ast_sockaddr_cmp(addr, &fd_addr);
+}
+#endif
+
+int ast_sd_get_fd(int type, const struct ast_sockaddr *addr)
+{
+#ifdef HAVE_SYSTEMD
+	int count = sd_listen_fds(0);
+	int idx;
+
+	for (idx = 0; idx < count; idx++) {
+		if (!ast_sd_is_socket_sockaddr(idx + SD_LISTEN_FDS_START, type, addr)) {
+			return idx + SD_LISTEN_FDS_START;
+		}
+	}
+#endif
+
+	return -1;
+}
+
+int ast_sd_get_fd_un(int type, const char *path)
+{
+#ifdef HAVE_SYSTEMD
+	int count = sd_listen_fds(0);
+	int idx;
+
+	for (idx = 0; idx < count; idx++) {
+		if (sd_is_socket_unix(idx + SD_LISTEN_FDS_START, type, 1, path, 0) > 0) {
+			return idx + SD_LISTEN_FDS_START;
+		}
+	}
+#endif
+
+	return -1;
+}
diff --git a/main/tcptls.c b/main/tcptls.c
index a3c7dfa7202d1f41386ac8a1e24fc75b7f21cdf9..85859a3435ca03fe8f736888f80dc1c3ef23f5ad 100644
--- a/main/tcptls.c
+++ b/main/tcptls.c
@@ -40,6 +40,7 @@
 
 #include "asterisk/compat.h"
 #include "asterisk/tcptls.h"
+#include "asterisk/io.h"
 #include "asterisk/http.h"
 #include "asterisk/utils.h"
 #include "asterisk/strings.h"
@@ -618,6 +619,7 @@ void ast_tcptls_server_start(struct ast_tcptls_session_args *desc)
 	int flags;
 	int x = 1;
 	int tls_changed = 0;
+	int sd_socket;
 
 	if (desc->tls_cfg) {
 		char hash[41];
@@ -689,6 +691,19 @@ void ast_tcptls_server_start(struct ast_tcptls_session_args *desc)
 		pthread_join(desc->master, NULL);
 	}
 
+	sd_socket = ast_sd_get_fd(SOCK_STREAM, &desc->local_address);
+
+	if (sd_socket != -1) {
+		if (desc->accept_fd != sd_socket) {
+			if (desc->accept_fd != -1) {
+				close(desc->accept_fd);
+			}
+			desc->accept_fd = sd_socket;
+		}
+
+		goto systemd_socket_activation;
+	}
+
 	if (desc->accept_fd != -1) {
 		close(desc->accept_fd);
 	}
@@ -718,6 +733,8 @@ void ast_tcptls_server_start(struct ast_tcptls_session_args *desc)
 		ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
 		goto error;
 	}
+
+systemd_socket_activation:
 	flags = fcntl(desc->accept_fd, F_GETFL);
 	fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
 	if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {