diff --git a/.gitignore b/.gitignore
index 54878dc755..4348244c5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -199,3 +199,6 @@ src/go/collectors/go.d.plugin/mocks/springboot2/.gradle/
 src/go/collectors/go.d.plugin/mocks/tmp/*
 !src/go/collectors/go.d.plugin/mocks/tmp/.gitkeep
 src/go/collectors/go.d.plugin/vendor
+
+# ignore nsis installer
+packaging/utils/netdata-installer.exe
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a8a9105b5e..79ea95a606 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -92,6 +92,9 @@ set(OS_MACOS       False)
 set(OS_WINDOWS     False)
 set(ALLOW_PLATFORM_SENSITIVE_OPTIONS True)
 
+set(NETDATA_RUNTIME_PREFIX "${CMAKE_INSTALL_PREFIX}")
+set(BINDIR usr/sbin)
+
 if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
         set(OS_MACOS True)
         find_library(IOKIT IOKit)
@@ -111,6 +114,16 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
         endif()
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "MSYS" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
         set(OS_WINDOWS True)
+
+        if(NOT "${CMAKE_INSTALL_PREFIX}" STREQUAL "/opt/netdata")
+                message(FATAL_ERROR "CMAKE_INSTALL_PREFIX must be set to /opt/netdata, but it is set to ${CMAKE_INSTALL_PREFIX}")
+        endif()
+
+        if(BUILD_FOR_PACKAGING)
+                set(NETDATA_RUNTIME_PREFIX "/")
+        endif()
+
+        set(BINDIR usr/bin)
         add_definitions(-D_GNU_SOURCE)
 
         if($ENV{CLION_IDE})
@@ -1434,6 +1447,7 @@ elseif(OS_FREEBSD)
 elseif(OS_WINDOWS)
         list(APPEND NETDATA_FILES
                 src/daemon/static_threads_windows.c
+                src/daemon/winsvc.cc
                 ${WINDOWS_PLUGIN_FILES}
                 ${INTERNAL_COLLECTORS_FILES}
         )
@@ -2279,7 +2293,7 @@ target_link_libraries(systemd-cat-native libnetdata)
 
 install(TARGETS systemd-cat-native
         COMPONENT netdata
-        DESTINATION usr/sbin)
+        DESTINATION "${BINDIR}")
 
 #
 # build log2journal
@@ -2314,7 +2328,7 @@ if(PCRE2_FOUND)
 
         install(TARGETS log2journal
                 COMPONENT netdata
-                DESTINATION usr/sbin)
+                DESTINATION "${BINDIR}")
 
         install(DIRECTORY src/collectors/log2journal/log2journal.d
                 COMPONENT netdata
@@ -2337,7 +2351,7 @@ target_link_libraries(netdatacli libnetdata)
 
 install(TARGETS netdatacli
         COMPONENT netdata
-        DESTINATION usr/sbin)
+        DESTINATION "${BINDIR}")
 
 #
 # Build go.d.plugin
@@ -2354,17 +2368,16 @@ endif()
 #
 # Generate config file
 #
-
-if(NOT CMAKE_INSTALL_PREFIX STREQUAL "")
-  string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
+if(NOT NETDATA_RUNTIME_PREFIX STREQUAL "")
+  string(REGEX REPLACE "/$" "" NETDATA_RUNTIME_PREFIX "${NETDATA_RUNTIME_PREFIX}")
 endif()
 
-set(CACHE_DIR "${CMAKE_INSTALL_PREFIX}/var/cache/netdata")
-set(CONFIG_DIR "${CMAKE_INSTALL_PREFIX}/etc/netdata")
-set(LIBCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/usr/lib/netdata/conf.d")
-set(LOG_DIR "${CMAKE_INSTALL_PREFIX}/var/log/netdata")
-set(PLUGINS_DIR "${CMAKE_INSTALL_PREFIX}/usr/libexec/netdata/plugins.d")
-set(VARLIB_DIR "${CMAKE_INSTALL_PREFIX}/var/lib/netdata")
+set(CACHE_DIR "${NETDATA_RUNTIME_PREFIX}/var/cache/netdata")
+set(CONFIG_DIR "${NETDATA_RUNTIME_PREFIX}/etc/netdata")
+set(LIBCONFIG_DIR "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/conf.d")
+set(LOG_DIR "${NETDATA_RUNTIME_PREFIX}/var/log/netdata")
+set(PLUGINS_DIR "${NETDATA_RUNTIME_PREFIX}/usr/libexec/netdata/plugins.d")
+set(VARLIB_DIR "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata")
 
 # A non-default value is only used when building Debian packages (/var/lib/netdata/www)
 if(NOT DEFINED WEB_DIR)
@@ -2373,7 +2386,7 @@ else()
   string(REGEX REPLACE "^/" "" WEB_DIR "${WEB_DIR}")
 endif()
 set(WEB_DEST "${WEB_DIR}")
-set(WEB_DIR "${CMAKE_INSTALL_PREFIX}/${WEB_DEST}")
+set(WEB_DIR "${NETDATA_RUNTIME_PREFIX}/${WEB_DEST}")
 
 
 set(CONFIGURE_COMMAND "dummy-configure-command")
@@ -2387,7 +2400,7 @@ configure_file(packaging/cmake/config.cmake.h.in config.h)
 # install
 #
 
-install(TARGETS netdata COMPONENT netdata DESTINATION usr/sbin)
+install(TARGETS netdata COMPONENT netdata DESTINATION "${BINDIR}")
 
 install(DIRECTORY COMPONENT netdata DESTINATION var/cache/netdata)
 install(DIRECTORY COMPONENT netdata DESTINATION var/log/netdata)
@@ -2406,15 +2419,15 @@ install(DIRECTORY COMPONENT netdata DESTINATION usr/lib/netdata/conf.d/schema.d)
 install(DIRECTORY COMPONENT netdata DESTINATION usr/libexec/netdata/plugins.d)
 install(DIRECTORY COMPONENT netdata DESTINATION ${WEB_DEST})
 
-set(libsysdir_POST "${CMAKE_INSTALL_PREFIX}/usr/lib/netdata/system")
-set(pkglibexecdir_POST "${CMAKE_INSTALL_PREFIX}/usr/libexec/netdata")
-set(localstatedir_POST "${CMAKE_INSTALL_PREFIX}/var")
-set(sbindir_POST "${CMAKE_INSTALL_PREFIX}/usr/sbin")
-set(configdir_POST "${CMAKE_INSTALL_PREFIX}/etc/netdata")
-set(libconfigdir_POST "${CMAKE_INSTALL_PREFIX}/usr/lib/netdata/conf.d")
-set(cachedir_POST "${CMAKE_INSTALL_PREFIX}/var/cache/netdata")
-set(registrydir_POST "${CMAKE_INSTALL_PREFIX}/var/lib/netdata/registry")
-set(varlibdir_POST "${CMAKE_INSTALL_PREFIX}/var/lib/netdata")
+set(libsysdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/system")
+set(pkglibexecdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/libexec/netdata")
+set(localstatedir_POST "${NETDATA_RUNTIME_PREFIX}/var")
+set(sbindir_POST "${NETDATA_RUNTIME_PREFIX}/${BINDIR}")
+set(configdir_POST "${NETDATA_RUNTIME_PREFIX}/etc/netdata")
+set(libconfigdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/conf.d")
+set(cachedir_POST "${NETDATA_RUNTIME_PREFIX}/var/cache/netdata")
+set(registrydir_POST "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata/registry")
+set(varlibdir_POST "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata")
 set(netdata_user_POST "${NETDATA_USER}")
 
 # netdata-claim.sh
@@ -2434,7 +2447,7 @@ configure_file(src/claim/netdata-claim.sh.in src/claim/netdata-claim.sh @ONLY)
 install(PROGRAMS
         ${CMAKE_BINARY_DIR}/src/claim/netdata-claim.sh
         COMPONENT netdata
-        DESTINATION usr/sbin)
+        DESTINATION "${BINDIR}")
 
 #
 # We don't check ENABLE_PLUGIN_CGROUP_NETWORK because rpm builds assume
@@ -3026,4 +3039,71 @@ if(NOT OS_WINDOWS)
                 DESTINATION ${WEB_DEST}/v0)
 endif()
 
+if(OS_WINDOWS)
+        install(FILES /usr/bin/awk.exe
+                      /usr/bin/bash.exe
+                      /usr/bin/cat.exe
+                      /usr/bin/chown.exe
+                      /usr/bin/curl.exe
+                      /usr/bin/env.exe
+                      /usr/bin/grep.exe
+                      /usr/bin/mkdir.exe
+                      /usr/bin/openssl.exe
+                      /usr/bin/rm.exe
+                      /usr/bin/sed.exe
+                      /usr/bin/sh.exe
+                      /usr/bin/tail.exe
+                      /usr/bin/tr.exe
+                      /usr/bin/uuidgen.exe
+                      /usr/bin/whoami.exe
+                DESTINATION "${BINDIR}")
+
+        install(FILES /usr/bin/msys-2.0.dll
+                      /usr/bin/msys-asn1-8.dll
+                      /usr/bin/msys-brotlicommon-1.dll
+                      /usr/bin/msys-brotlidec-1.dll
+                      /usr/bin/msys-brotlienc-1.dll
+                      /usr/bin/msys-com_err-1.dll
+                      /usr/bin/msys-crypt-2.dll
+                      /usr/bin/msys-crypto-3.dll
+                      /usr/bin/msys-curl-4.dll
+                      /usr/bin/msys-gcc_s-seh-1.dll
+                      /usr/bin/msys-gmp-10.dll
+                      /usr/bin/msys-gssapi-3.dll
+                      /usr/bin/msys-hcrypto-4.dll
+                      /usr/bin/msys-heimbase-1.dll
+                      /usr/bin/msys-heimntlm-0.dll
+                      /usr/bin/msys-hx509-5.dll
+                      /usr/bin/msys-iconv-2.dll
+                      /usr/bin/msys-idn2-0.dll
+                      /usr/bin/msys-intl-8.dll
+                      /usr/bin/msys-krb5-26.dll
+                      /usr/bin/msys-lz4-1.dll
+                      /usr/bin/msys-mpfr-6.dll
+                      /usr/bin/msys-ncursesw6.dll
+                      /usr/bin/msys-nghttp2-14.dll
+                      /usr/bin/msys-pcre-1.dll
+                      /usr/bin/msys-protobuf-32.dll
+                      /usr/bin/msys-psl-5.dll
+                      /usr/bin/msys-readline8.dll
+                      /usr/bin/msys-roken-18.dll
+                      /usr/bin/msys-sqlite3-0.dll
+                      /usr/bin/msys-ssh2-1.dll
+                      /usr/bin/msys-ssl-3.dll
+                      /usr/bin/msys-stdc++-6.dll
+                      /usr/bin/msys-unistring-5.dll
+                      /usr/bin/msys-uuid-1.dll
+                      /usr/bin/msys-uv-1.dll
+                      /usr/bin/msys-wind-0.dll
+                      /usr/bin/msys-z.dll
+                      /usr/bin/msys-zstd-1.dll
+                DESTINATION "${BINDIR}")
+
+        # Make bash & netdata happy
+        install(DIRECTORY DESTINATION tmp)
+
+        # Make curl work with ssl
+        install(DIRECTORY /usr/ssl DESTINATION usr)
+endif()
+
 include(Packaging)
diff --git a/packaging/utils/compile-on-windows.sh b/packaging/utils/compile-on-windows.sh
index aa44771b8e..103052be49 100644
--- a/packaging/utils/compile-on-windows.sh
+++ b/packaging/utils/compile-on-windows.sh
@@ -16,7 +16,8 @@ install_dependencies() {
         msys/pcre2-devel mingw64/mingw-w64-x86_64-pcre2 ucrt64/mingw-w64-ucrt-x86_64-pcre2 \
         msys/brotli-devel mingw64/mingw-w64-x86_64-brotli ucrt64/mingw-w64-ucrt-x86_64-brotli \
         msys/ccache ucrt64/mingw-w64-ucrt-x86_64-ccache mingw64/mingw-w64-x86_64-ccache \
-        mingw64/mingw-w64-x86_64-go ucrt64/mingw-w64-ucrt-x86_64-go
+        mingw64/mingw-w64-x86_64-go ucrt64/mingw-w64-ucrt-x86_64-go \
+        mingw64/mingw-w64-x86_64-nsis
 }
 
 if [ "${1}" = "install" ]
@@ -52,7 +53,9 @@ fi
     -G Ninja \
     -DCMAKE_INSTALL_PREFIX="/opt/netdata" \
     -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
-    -DCMAKE_C_FLAGS="-O0 -ggdb -Wall -Wextra -Wno-char-subscripts -Wa,-mbig-obj -pipe -DNETDATA_INTERNAL_CHECKS=1 -D_FILE_OFFSET_BITS=64 -D__USE_MINGW_ANSI_STDIO=1" \
+    -DCMAKE_C_FLAGS="-fstack-protector-all -O0 -ggdb -Wall -Wextra -Wno-char-subscripts -Wa,-mbig-obj -pipe -DNETDATA_INTERNAL_CHECKS=1 -D_FILE_OFFSET_BITS=64 -D__USE_MINGW_ANSI_STDIO=1" \
+    -DBUILD_FOR_PACKAGING=On \
+    -DUSE_MOLD=Off \
     -DNETDATA_USER="${USER}" \
     -DDEFAULT_FEATURE_STATE=Off \
     -DENABLE_H2O=Off \
diff --git a/packaging/utils/find-dll-deps.sh b/packaging/utils/find-dll-deps.sh
new file mode 100644
index 0000000000..9f4fe38475
--- /dev/null
+++ b/packaging/utils/find-dll-deps.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+if [ "$#" -lt 1 ]; then
+    echo "Usage: $0 <command1> <command2> ... <commandN>"
+    exit 1
+fi
+
+results=()
+
+for arg in "$@"; do
+    while IFS= read -r line; do
+      results+=("$line")
+    done < <(ldd "$arg" | grep /usr/bin | awk '{ print $3 }')
+done
+
+printf "%s\n" "${results[@]}" | sort | uniq
diff --git a/packaging/utils/installer.nsi b/packaging/utils/installer.nsi
index b78f52ffe2..10600f15eb 100644
--- a/packaging/utils/installer.nsi
+++ b/packaging/utils/installer.nsi
@@ -1,34 +1,58 @@
-Outfile "netdata-installer.exe"
-InstallDir "C:\netdata"
+!include "MUI2.nsh"
 
+Name "Netdata"
+Outfile "netdata-installer.exe"
+InstallDir "$PROGRAMFILES\netdata"
 RequestExecutionLevel admin
 
-Section
-	SetOutPath $INSTDIR
-	WriteUninstaller $INSTDIR\uninstaller.exe
-SectionEnd
+!define MUI_ABORTWARNING
+!define MUI_UNABORTWARNING
 
-Section "Install MSYS2 environment"
-	SetOutPath $TEMP
+!insertmacro MUI_PAGE_WELCOME
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_PAGE_FINISH
 
-	SetCompress off
-	File "C:\msys64\msys2-installer.exe"
-    nsExec::ExecToLog 'cmd.exe /C "$TEMP\msys2-installer.exe" in --confirm-command --accept-messages --root $INSTDIR'
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+!insertmacro MUI_UNPAGE_FINISH
 
-	Delete "$TEMP\msys2-installer.exe"
-SectionEnd
-
-Section "Install MSYS2 packages"
-	ExecWait '"$INSTDIR\usr\bin\bash.exe" -lc "pacman -S --noconfirm msys/libuv msys/protobuf"'
-SectionEnd
+!insertmacro MUI_LANGUAGE "English"
 
 Section "Install Netdata"
-	SetOutPath $INSTDIR\opt\netdata
-
+	SetOutPath $INSTDIR
 	SetCompress off
+
 	File /r "C:\msys64\opt\netdata\*.*"
+
+	ClearErrors
+	ExecWait '"$SYSDIR\sc.exe" create Netdata binPath= "$INSTDIR\usr\bin\netdata.exe" start= delayed-auto'
+	IfErrors 0 +2
+	DetailPrint "Warning: Failed to create Netdata service."
+
+	ClearErrors
+	ExecWait '"$SYSDIR\sc.exe" description Netdata "Real-time system monitoring service"'
+	IfErrors 0 +2
+	DetailPrint "Warning: Failed to add Netdata service description."
+
+	ClearErrors
+	ExecWait '"$SYSDIR\sc.exe" start Netdata'
+	IfErrors 0 +2
+	DetailPrint "Warning: Failed to start Netdata service."
+
+	WriteUninstaller "$INSTDIR\Uninstall.exe"
 SectionEnd
 
 Section "Uninstall"
-	nsExec::ExecToLog 'cmd.exe /C "$INSTDIR\uninstall.exe" pr --confirm-command'
+	ClearErrors
+	ExecWait '"$SYSDIR\sc.exe" stop Netdata'
+	IfErrors 0 +2
+	DetailPrint "Warning: Failed to stop Netdata service."
+
+	ClearErrors
+	ExecWait '"$SYSDIR\sc.exe" delete Netdata'
+	IfErrors 0 +2
+	DetailPrint "Warning: Failed to delete Netdata service."
+
+	RMDir /r "$INSTDIR"
 SectionEnd
diff --git a/src/daemon/analytics.c b/src/daemon/analytics.c
index 33f6f357f6..e9abf11c88 100644
--- a/src/daemon/analytics.c
+++ b/src/daemon/analytics.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-3.0-or-later
 
+#include "analytics.h"
 #include "common.h"
 #include "buildinfo.h"
 
@@ -470,8 +471,6 @@ void analytics_alarms(void)
  */
 void analytics_misc(void)
 {
-    spinlock_init(&analytics_data.spinlock);
-
 #ifdef ENABLE_ACLK
     analytics_set_data(&analytics_data.netdata_host_cloud_available, "true");
     analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "Next Generation");
@@ -1081,3 +1080,8 @@ void analytics_statistic_send(const analytics_statistic_t *statistic) {
 
     freez(command_to_run);
 }
+
+void analytics_init(void)
+{
+    spinlock_init(&analytics_data.spinlock);
+}
diff --git a/src/daemon/analytics.h b/src/daemon/analytics.h
index 501eb7b555..747cf6070e 100644
--- a/src/daemon/analytics.h
+++ b/src/daemon/analytics.h
@@ -86,6 +86,7 @@ void analytics_log_dashboard(void);
 void analytics_gather_mutable_meta_data(void);
 void analytics_report_oom_score(long long int score);
 void get_system_timezone(void);
+void analytics_init(void);
 
 typedef struct {
     const char *action;
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 2678d955c3..75fa13356e 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -496,6 +496,10 @@ void netdata_cleanup_and_exit(int ret, const char *action, const char *action_re
     watcher_shutdown_end();
     watcher_thread_stop();
 
+#ifdef OS_WINDOWS
+    return;
+#endif
+
 #ifdef ENABLE_SENTRY
     if (ret)
         abort();
@@ -1460,7 +1464,11 @@ int unittest_prepare_rrd(char **user) {
     return 0;
 }
 
-int main(int argc, char **argv) {
+int netdata_main(int argc, char **argv)
+{
+    analytics_init();
+    string_init();
+
     // initialize the system clocks
     clocks_init();
     netdata_start_time = now_realtime_sec();
@@ -1471,11 +1479,16 @@ int main(int argc, char **argv) {
 
     int i;
     int config_loaded = 0;
-    int dont_fork = 0;
     bool close_open_fds = true;
     size_t default_stacksize;
     char *user = NULL;
 
+#ifdef OS_WINDOWS
+    int dont_fork = 1;
+#else
+    int dont_fork = 0;
+#endif
+
     static_threads = static_threads_get();
 
     netdata_ready = false;
@@ -2214,7 +2227,10 @@ int main(int argc, char **argv) {
 
     // fork the spawn server
     delta_startup_time("fork the spawn server");
+
+#ifndef OS_WINDOWS
     spawn_init();
+#endif
 
     /*
      * Libuv uv_spawn() uses SIGCHLD internally:
@@ -2357,22 +2373,21 @@ int main(int argc, char **argv) {
     }
 #endif
 
-    // ------------------------------------------------------------------------
-    // initialize WebRTC
-
     webrtc_initialize();
 
-    // ------------------------------------------------------------------------
-    // unblock signals
-
     signals_unblock();
 
-    // ------------------------------------------------------------------------
-    // Handle signals
+    return 10;
+}
+
+#ifndef OS_WINDOWS
+int main(int argc, char *argv[])
+{
+    int rc = netdata_main(argc, argv);
+    if (rc != 10)
+        return rc;
 
     signals_handle();
-
-    // should never reach this point
-    // but we need it for rpmlint #2752
     return 1;
 }
+#endif
diff --git a/src/daemon/static_threads.c b/src/daemon/static_threads.c
index 2667c0fb5c..1a5f82f7f2 100644
--- a/src/daemon/static_threads.c
+++ b/src/daemon/static_threads.c
@@ -30,7 +30,7 @@ const struct netdata_static_thread static_threads_common[] = {
         .name = "HEALTH",
         .config_section = NULL,
         .config_name = NULL,
-        .enabled = 1,
+        .enabled = 0,
         .thread = NULL,
         .init_routine = NULL,
         .start_routine = health_main
@@ -70,7 +70,11 @@ const struct netdata_static_thread static_threads_common[] = {
         .name = "PLUGINSD",
         .config_section = NULL,
         .config_name = NULL,
+#ifdef OS_WINDOWS
+        .enabled = 0,
+#else
         .enabled = 1,
+#endif
         .thread = NULL,
         .init_routine = NULL,
         .start_routine = pluginsd_main
diff --git a/src/daemon/winsvc.cc b/src/daemon/winsvc.cc
new file mode 100644
index 0000000000..cdf503256c
--- /dev/null
+++ b/src/daemon/winsvc.cc
@@ -0,0 +1,252 @@
+extern "C" {
+
+#include "daemon.h"
+#include "libnetdata/libnetdata.h"
+
+int netdata_main(int argc, char *argv[]);
+void signals_handle(void);
+
+}
+
+#include <windows.h>
+
+__attribute__((format(printf, 1, 2)))
+static void netdata_service_log(const char *fmt, ...)
+{
+    char path[FILENAME_MAX + 1];
+    snprintfz(path, FILENAME_MAX, "%s/service.log", LOG_DIR);
+
+    FILE *fp = fopen(path, "a");
+    if (fp == NULL) {
+        return;
+    }
+
+    SYSTEMTIME time;
+    GetSystemTime(&time);
+    fprintf(fp, "%d:%d:%d - ", time.wHour, time.wMinute, time.wSecond);
+
+    va_list args;
+    va_start(args, fmt);
+    vfprintf(fp, fmt, args);
+    va_end(args);
+
+    fprintf(fp, "\n");
+
+    fflush(fp);
+    fclose(fp);
+}
+
+static SERVICE_STATUS_HANDLE svc_status_handle = nullptr;
+static SERVICE_STATUS svc_status = {};
+
+static HANDLE svc_stop_event_handle = nullptr;
+
+static ND_THREAD *cleanup_thread = nullptr;
+
+static bool ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint, DWORD dwControlsAccepted)
+{
+    static DWORD dwCheckPoint = 1;
+    svc_status.dwCurrentState = dwCurrentState;
+    svc_status.dwWin32ExitCode = dwWin32ExitCode;
+    svc_status.dwWaitHint = dwWaitHint;
+    svc_status.dwControlsAccepted = dwControlsAccepted;
+
+    if (dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED)
+    {
+        svc_status.dwCheckPoint = 0;
+    }
+    else
+    {
+        svc_status.dwCheckPoint = dwCheckPoint++;
+    }
+
+    if (!SetServiceStatus(svc_status_handle, &svc_status)) {
+        netdata_service_log("@ReportSvcStatus: SetServiceStatusFailed (%d)", GetLastError());
+        return false;
+    }
+
+    return true;
+}
+
+static HANDLE CreateEventHandle(const char *msg)
+{
+    HANDLE h = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+    if (!h)
+    {
+        netdata_service_log(msg);
+
+        if (!ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 1000, 0))
+        {
+            netdata_service_log("Failed to set service status to stopped.");
+        }
+
+        return NULL;
+    }
+
+    return h;
+}
+
+static void *call_netdata_cleanup(void *arg)
+{
+    UNUSED(arg);
+
+    // Wait until we have to stop the service
+    netdata_service_log("Cleanup thread waiting for stop event...");
+    WaitForSingleObject(svc_stop_event_handle, INFINITE);
+
+    // Stop the agent
+    netdata_service_log("Running netdata cleanup...");
+    netdata_cleanup_and_exit(0, NULL, NULL, NULL);
+
+    // Close event handle
+    netdata_service_log("Closing stop event handle...");
+    CloseHandle(svc_stop_event_handle);
+
+    // Set status to stopped
+    netdata_service_log("Reporting the service as stopped...");
+    ReportSvcStatus(SERVICE_STOPPED, 0, 0, 0);
+
+    return nullptr;
+}
+
+static void WINAPI ServiceControlHandler(DWORD controlCode)
+{
+    switch (controlCode)
+    {
+        case SERVICE_CONTROL_STOP:
+        {
+            if (svc_status.dwCurrentState != SERVICE_RUNNING)
+                return;
+
+            // Set service status to stop-pending
+            netdata_service_log("Setting service status to stop-pending...");
+            if (!ReportSvcStatus(SERVICE_STOP_PENDING, 0, 5000, 0))
+                return;
+
+            // Create cleanup thread
+            netdata_service_log("Creating cleanup thread...");
+            char tag[NETDATA_THREAD_TAG_MAX + 1];
+            snprintfz(tag, NETDATA_THREAD_TAG_MAX, "%s", "CLEANUP");
+            cleanup_thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_JOINABLE, call_netdata_cleanup, NULL);
+
+            // Signal the stop request
+            netdata_service_log("Signalling the cleanup thread...");
+            SetEvent(svc_stop_event_handle);
+            break;
+        }
+        case SERVICE_CONTROL_INTERROGATE:
+        {
+            ReportSvcStatus(svc_status.dwCurrentState, svc_status.dwWin32ExitCode, svc_status.dwWaitHint, svc_status.dwControlsAccepted);
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+void WINAPI ServiceMain(DWORD argc, LPSTR* argv)
+{
+    UNUSED(argc);
+    UNUSED(argv);
+
+    // Create service status handle
+    netdata_service_log("Creating service status handle...");
+    svc_status_handle = RegisterServiceCtrlHandler("Netdata", ServiceControlHandler);
+    if (!svc_status_handle)
+    {
+        netdata_service_log("@ServiceMain() - RegisterServiceCtrlHandler() failed...");
+        return;
+    }
+
+    // Set status to start-pending
+    netdata_service_log("Setting service status to start-pending...");
+    svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+    svc_status.dwServiceSpecificExitCode = 0;
+    svc_status.dwCheckPoint = 0;
+    if (!ReportSvcStatus(SERVICE_START_PENDING, 0, 5000, 0))
+    {
+        netdata_service_log("Failed to set service status to start pending.");
+        return;
+    }
+
+    // Create stop service event handle
+    netdata_service_log("Creating stop service event handle...");
+    svc_stop_event_handle = CreateEventHandle("Failed to create stop event handle");
+    if (!svc_stop_event_handle)
+        return;
+
+    // Set status to running
+    netdata_service_log("Setting service status to running...");
+    if (!ReportSvcStatus(SERVICE_RUNNING, 0, 5000, SERVICE_ACCEPT_STOP))
+    {
+        netdata_service_log("Failed to set service status to running.");
+        return;
+    }
+
+    // Run the agent
+    netdata_service_log("Running the agent...");
+    netdata_main(argc, argv);
+
+    netdata_service_log("Agent has been started...");
+}
+
+static bool update_path() {
+    const char *old_path = getenv("PATH");
+
+    if (!old_path) {
+        if (setenv("PATH", "/usr/bin", 1) != 0) {
+            netdata_service_log("Failed to set PATH to /usr/bin");
+            return false;
+        }
+
+        return true;
+    }
+
+    size_t new_path_length = strlen(old_path) + strlen("/usr/bin") + 2;
+    char *new_path = (char *) callocz(new_path_length, sizeof(char));
+    snprintfz(new_path, new_path_length, "/usr/bin:%s", old_path);
+
+    if (setenv("PATH", new_path, 1) != 0) {
+        netdata_service_log("Failed to add /usr/bin to PATH");
+        freez(new_path);
+        return false;
+    }
+
+    freez(new_path);
+    return true;
+}
+
+int main(int argc, char *argv[])
+{
+    bool tty = isatty(fileno(stdout)) == 1;
+
+    if (!update_path()) {
+        return 1;
+    }
+
+    if (tty)
+    {
+        int rc = netdata_main(argc, argv);
+        if (rc != 10)
+            return rc;
+
+        signals_handle();
+        return 1;
+    }
+    else
+    {
+        SERVICE_TABLE_ENTRY serviceTable[] = {
+            { strdupz("Netdata"), ServiceMain },
+            { nullptr, nullptr }
+        };
+
+        if (!StartServiceCtrlDispatcher(serviceTable))
+        {
+            netdata_service_log("@main() - StartServiceCtrlDispatcher() failed...");
+            return 1;
+        }
+
+        return 0;
+    }
+}
diff --git a/src/database/rrdhost.c b/src/database/rrdhost.c
index 6bf2c25518..dd5f2a43e2 100644
--- a/src/database/rrdhost.c
+++ b/src/database/rrdhost.c
@@ -935,7 +935,13 @@ void dbengine_init(char *hostname) {
         config_set_number(CONFIG_SECTION_DB, "dbengine tier 0 disk space MB", default_multidb_disk_quota_mb);
     }
 
+#ifdef OS_WINDOWS
+    // FIXME: for whatever reason joining the initialization threads
+    // fails on Windows.
+    bool parallel_initialization = false;
+#else
     bool parallel_initialization = (storage_tiers <= (size_t)get_netdata_cpus()) ? true : false;
+#endif
 
     struct dbengine_initialization tiers_init[RRD_STORAGE_TIERS] = {};
 
diff --git a/src/libnetdata/clocks/clocks.c b/src/libnetdata/clocks/clocks.c
index e1a3e64cb3..a681689623 100644
--- a/src/libnetdata/clocks/clocks.c
+++ b/src/libnetdata/clocks/clocks.c
@@ -368,6 +368,35 @@ usec_t heartbeat_next(heartbeat_t *hb, usec_t tick) {
     return dt;
 }
 
+#ifdef OS_WINDOWS
+
+#include "windows.h"
+
+void sleep_usec_with_now(usec_t usec, usec_t started_ut)
+{
+    if (!started_ut)
+        started_ut = now_realtime_usec();
+
+    usec_t end_ut = started_ut + usec;
+    usec_t remaining_ut = usec;
+
+    timeBeginPeriod(1);
+
+    while (remaining_ut >= 1000)
+    {
+        DWORD sleep_ms = (DWORD) (remaining_ut / USEC_PER_MS);
+        Sleep(sleep_ms);
+
+        usec_t now_ut = now_realtime_usec();
+        if (now_ut >= end_ut)
+            break;
+
+        remaining_ut = end_ut - now_ut;
+    }
+
+    timeEndPeriod(1);
+}
+#else
 void sleep_usec_with_now(usec_t usec, usec_t started_ut) {
     // we expect microseconds (1.000.000 per second)
     // but timespec is nanoseconds (1.000.000.000 per second)
@@ -411,6 +440,7 @@ void sleep_usec_with_now(usec_t usec, usec_t started_ut) {
         }
     }
 }
+#endif
 
 static inline collected_number uptime_from_boottime(void) {
 #ifdef CLOCK_BOOTTIME_IS_AVAILABLE
diff --git a/src/libnetdata/libnetdata.h b/src/libnetdata/libnetdata.h
index 859f54cc33..1c72e54106 100644
--- a/src/libnetdata/libnetdata.h
+++ b/src/libnetdata/libnetdata.h
@@ -451,7 +451,12 @@ typedef enum {
 } OPEN_FD_EXCLUDE;
 void for_each_open_fd(OPEN_FD_ACTION action, OPEN_FD_EXCLUDE excluded_fds);
 
+#ifdef OS_WINDOWS
+void netdata_cleanup_and_exit(int ret, const char *action, const char *action_result, const char *action_data);
+#else
 void netdata_cleanup_and_exit(int ret, const char *action, const char *action_result, const char *action_data) NORETURN;
+#endif
+
 extern char *netdata_configured_host_prefix;
 
 #include "os/os.h"
diff --git a/src/libnetdata/locks/locks.c b/src/libnetdata/locks/locks.c
index d01ee29f14..424b86ce95 100644
--- a/src/libnetdata/locks/locks.c
+++ b/src/libnetdata/locks/locks.c
@@ -224,24 +224,35 @@ int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock) {
 // spinlock implementation
 // https://www.youtube.com/watch?v=rmGJc9PXpuE&t=41s
 
-void spinlock_init(SPINLOCK *spinlock) {
+#ifdef SPINLOCK_IMPL_WITH_MUTEX
+void spinlock_init(SPINLOCK *spinlock)
+{
+    netdata_mutex_init(&spinlock->inner);
+}
+#else
+void spinlock_init(SPINLOCK *spinlock)
+{
     memset(spinlock, 0, sizeof(SPINLOCK));
 }
-
-static inline void spinlock_lock_internal(SPINLOCK *spinlock) {
-#ifdef NETDATA_INTERNAL_CHECKS
-    size_t spins = 0;
 #endif
 
+#ifndef SPINLOCK_IMPL_WITH_MUTEX
+static inline void spinlock_lock_internal(SPINLOCK *spinlock)
+{
+    #ifdef NETDATA_INTERNAL_CHECKS
+    size_t spins = 0;
+    #endif
+
     for(int i = 1;
         __atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) ||
         __atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)
         ; i++
         ) {
 
-#ifdef NETDATA_INTERNAL_CHECKS
+        #ifdef NETDATA_INTERNAL_CHECKS
         spins++;
-#endif
+        #endif
+
         if(unlikely(i == 8)) {
             i = 0;
             tinysleep();
@@ -250,23 +261,29 @@ static inline void spinlock_lock_internal(SPINLOCK *spinlock) {
 
     // we have the lock
 
-#ifdef NETDATA_INTERNAL_CHECKS
+    #ifdef NETDATA_INTERNAL_CHECKS
     spinlock->spins += spins;
     spinlock->locker_pid = gettid_cached();
-#endif
+    #endif
 
     nd_thread_spinlock_locked();
 }
+#endif // SPINLOCK_IMPL_WITH_MUTEX
 
-static inline void spinlock_unlock_internal(SPINLOCK *spinlock) {
-#ifdef NETDATA_INTERNAL_CHECKS
+#ifndef SPINLOCK_IMPL_WITH_MUTEX
+static inline void spinlock_unlock_internal(SPINLOCK *spinlock)
+{
+    #ifdef NETDATA_INTERNAL_CHECKS
     spinlock->locker_pid = 0;
-#endif
+    #endif
+
     __atomic_clear(&spinlock->locked, __ATOMIC_RELEASE);
 
     nd_thread_spinlock_unlocked();
 }
+#endif // SPINLOCK_IMPL_WITH_MUTEX
 
+#ifndef SPINLOCK_IMPL_WITH_MUTEX
 static inline bool spinlock_trylock_internal(SPINLOCK *spinlock) {
     if(!__atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) &&
         !__atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)) {
@@ -277,36 +294,79 @@ static inline bool spinlock_trylock_internal(SPINLOCK *spinlock) {
 
     return false;
 }
+#endif // SPINLOCK_IMPL_WITH_MUTEX
 
+#ifdef SPINLOCK_IMPL_WITH_MUTEX
+void spinlock_lock(SPINLOCK *spinlock)
+{
+    netdata_mutex_lock(&spinlock->inner);
+}
+#else
 void spinlock_lock(SPINLOCK *spinlock)
 {
     spinlock_lock_internal(spinlock);
 }
+#endif
 
+#ifdef SPINLOCK_IMPL_WITH_MUTEX
+void spinlock_unlock(SPINLOCK *spinlock)
+{
+    netdata_mutex_unlock(&spinlock->inner);
+}
+#else
 void spinlock_unlock(SPINLOCK *spinlock)
 {
     spinlock_unlock_internal(spinlock);
 }
+#endif
 
+#ifdef SPINLOCK_IMPL_WITH_MUTEX
+bool spinlock_trylock(SPINLOCK *spinlock)
+{
+    return netdata_mutex_trylock(&spinlock->inner) == 0;
+}
+#else
 bool spinlock_trylock(SPINLOCK *spinlock)
 {
     return spinlock_trylock_internal(spinlock);
 }
+#endif
 
+#ifdef SPINLOCK_IMPL_WITH_MUTEX
+void spinlock_lock_cancelable(SPINLOCK *spinlock)
+{
+    netdata_mutex_lock(&spinlock->inner);
+}
+#else
 void spinlock_lock_cancelable(SPINLOCK *spinlock)
 {
     spinlock_lock_internal(spinlock);
 }
+#endif
 
+#ifdef SPINLOCK_IMPL_WITH_MUTEX
+void spinlock_unlock_cancelable(SPINLOCK *spinlock)
+{
+    netdata_mutex_unlock(&spinlock->inner);
+}
+#else
 void spinlock_unlock_cancelable(SPINLOCK *spinlock)
 {
     spinlock_unlock_internal(spinlock);
 }
+#endif
 
+#ifdef SPINLOCK_IMPL_WITH_MUTEX
+bool spinlock_trylock_cancelable(SPINLOCK *spinlock)
+{
+    return netdata_mutex_trylock(&spinlock->inner) == 0;
+}
+#else
 bool spinlock_trylock_cancelable(SPINLOCK *spinlock)
 {
     return spinlock_trylock_internal(spinlock);
 }
+#endif
 
 // ----------------------------------------------------------------------------
 // rw_spinlock implementation
diff --git a/src/libnetdata/locks/locks.h b/src/libnetdata/locks/locks.h
index d3873c2952..c05c65fe2d 100644
--- a/src/libnetdata/locks/locks.h
+++ b/src/libnetdata/locks/locks.h
@@ -6,19 +6,34 @@
 #include "../libnetdata.h"
 #include "../clocks/clocks.h"
 
+// #ifdef OS_WINDOWS
+// #define SPINLOCK_IMPL_WITH_MUTEX
+// #endif
+
 typedef pthread_mutex_t netdata_mutex_t;
 #define NETDATA_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
 
-typedef struct netdata_spinlock {
-    bool locked;
-#ifdef NETDATA_INTERNAL_CHECKS
-    pid_t locker_pid;
-    size_t spins;
+#ifdef SPINLOCK_IMPL_WITH_MUTEX
+    typedef struct netdata_spinlock
+    {
+        netdata_mutex_t inner;
+    } SPINLOCK;
+#else
+    typedef struct netdata_spinlock
+    {
+        bool locked;
+    #ifdef NETDATA_INTERNAL_CHECKS
+        pid_t locker_pid;
+        size_t spins;
+    #endif
+    } SPINLOCK;
 #endif
-} SPINLOCK;
 
-#define NETDATA_SPINLOCK_INITIALIZER \
-    { .locked = false }
+#ifdef SPINLOCK_IMPL_WITH_MUTEX
+#define NETDATA_SPINLOCK_INITIALIZER { .inner = PTHREAD_MUTEX_INITIALIZER }
+#else
+#define NETDATA_SPINLOCK_INITIALIZER { .locked = false }
+#endif
 
 void spinlock_init(SPINLOCK *spinlock);
 void spinlock_lock(SPINLOCK *spinlock);
diff --git a/src/libnetdata/string/string.c b/src/libnetdata/string/string.c
index 94c11f4b9a..257a3cc4b9 100644
--- a/src/libnetdata/string/string.c
+++ b/src/libnetdata/string/string.c
@@ -702,3 +702,8 @@ int string_unittest(size_t entries) {
     fprintf(stderr, "\n%zu errors found\n", errors);
     return  errors ? 1 : 0;
 }
+
+void string_init(void) {
+    for (size_t i = 0; i != STRING_PARTITIONS; i++)
+        rw_spinlock_init(&string_base[i].spinlock);
+}
diff --git a/src/libnetdata/string/string.h b/src/libnetdata/string/string.h
index f2ff9666ca..c44696be25 100644
--- a/src/libnetdata/string/string.h
+++ b/src/libnetdata/string/string.h
@@ -34,4 +34,6 @@ void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_
 
 int string_unittest(size_t entries);
 
+void string_init(void);
+
 #endif
diff --git a/src/libnetdata/threads/threads.c b/src/libnetdata/threads/threads.c
index 0e12d173ec..36c63f4e0f 100644
--- a/src/libnetdata/threads/threads.c
+++ b/src/libnetdata/threads/threads.c
@@ -418,12 +418,14 @@ bool nd_thread_signaled_to_cancel(void) {
 // ----------------------------------------------------------------------------
 // nd_thread_join
 
-void nd_thread_join(ND_THREAD *nti) {
-    if(!nti) return;
+int nd_thread_join(ND_THREAD *nti) {
+    if(!nti)
+        return ESRCH;
 
     int ret = pthread_join(nti->thread, NULL);
-    if(ret != 0)
-        nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot join thread. pthread_join() failed with code %d.", ret);
+    if(ret != 0) {
+        nd_log(NDLS_DAEMON, NDLP_WARNING, "cannot join thread. pthread_join() failed with code %d. (tag=%s)", ret, nti->tag);
+    }
     else {
         nd_thread_status_set(nti, NETDATA_THREAD_STATUS_JOINED);
 
@@ -434,4 +436,6 @@ void nd_thread_join(ND_THREAD *nti) {
 
         freez(nti);
     }
+
+    return ret;
 }
diff --git a/src/libnetdata/threads/threads.h b/src/libnetdata/threads/threads.h
index a7204e2a2f..0b54a5fc0b 100644
--- a/src/libnetdata/threads/threads.h
+++ b/src/libnetdata/threads/threads.h
@@ -70,7 +70,7 @@ void netdata_threads_init_after_fork(size_t stacksize);
 void netdata_threads_init_for_external_plugins(size_t stacksize);
 
 ND_THREAD *nd_thread_create(const char *tag, NETDATA_THREAD_OPTIONS options, void *(*start_routine) (void *), void *arg);
-void nd_thread_join(ND_THREAD * nti);
+int nd_thread_join(ND_THREAD * nti);
 ND_THREAD *nd_thread_self(void);
 bool nd_thread_is_me(ND_THREAD *nti);