diff --git a/appveyor.yml b/appveyor.yml
index ef27319518323252c10bc9235c3e8eb8f1e1cd36..03ffdca9da841a51685ee339e58ca6461991db8e 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,39 +1,40 @@
 os: Visual Studio 2015
 
 # Clone directly into GOPATH.
-clone_folder: c:\gopath\src\github.com\ethereum\go-ethereum
+clone_folder: C:\gopath\src\github.com\ethereum\go-ethereum
 clone_depth: 5
 version: "{branch}.{build}"
 environment:
   global:
-    GOPATH: c:\gopath
+    GOPATH: C:\gopath
     CC: gcc.exe
   matrix:
     - GETH_ARCH: amd64
       MSYS2_ARCH: x86_64
       MSYS2_BITS: 64
       MSYSTEM: MINGW64
-      PATH: C:\msys64\mingw64\bin\;%PATH%
+      PATH: C:\msys64\mingw64\bin\;C:\Program Files (x86)\NSIS\;%PATH%
     - GETH_ARCH: 386
       MSYS2_ARCH: i686
       MSYS2_BITS: 32
       MSYSTEM: MINGW32
-      PATH: C:\msys64\mingw32\bin\;%PATH%
+      PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH%
 
 install:
-  - rmdir c:\go /s /q
+  - rmdir C:\go /s /q
   - appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.3.windows-amd64.zip
   - 7z x go1.7.3.windows-amd64.zip -y -oC:\ > NUL
   - go version
   - gcc --version
 
 build_script:
-  - go run build\\ci.go install -arch %GETH_ARCH%
+  - go run build\ci.go install -arch %GETH_ARCH%
 
 after_build:
-  - go run build\\ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
+  - go run build\ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
+  - go run build\ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
 
 test_script:
   - set GOARCH=%GETH_ARCH%
   - set CGO_ENABLED=1
-  - go run build\\ci.go test -vet -coverage
+  - go run build\ci.go test -vet -coverage
diff --git a/build/ci.go b/build/ci.go
index 2c6e918a6ab794cc55a128f5efe4c8268fb6a2a8..f5ef54b7545d51916cfac6705fca9e0b653e879e 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -28,6 +28,7 @@ Available commands are:
    archive    [-arch architecture] [ -type zip|tar ] [ -signer key-envvar ] [ -upload dest ] -- archives build artefacts
    importkeys                                                                                -- imports signing keys from env
    debsrc     [ -signer key-id ] [ -upload dest ]                                            -- creates a debian source package
+   nsis                                                                                      -- creates a Windows NSIS installer
    xgo        [ options ]                                                                    -- cross builds according to options
 
 For all commands, -n prevents execution of external programs (dry run mode).
@@ -122,6 +123,8 @@ func main() {
 		doArchive(os.Args[2:])
 	case "debsrc":
 		doDebianSource(os.Args[2:])
+	case "nsis":
+		doWindowsInstaller(os.Args[2:])
 	case "xgo":
 		doXgo(os.Args[2:])
 	default:
@@ -429,7 +432,7 @@ func makeWorkdir(wdflag string) string {
 	if wdflag != "" {
 		err = os.MkdirAll(wdflag, 0744)
 	} else {
-		wdflag, err = ioutil.TempDir("", "eth-deb-build-")
+		wdflag, err = ioutil.TempDir("", "geth-build-")
 	}
 	if err != nil {
 		log.Fatal(err)
@@ -559,6 +562,76 @@ func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) {
 	return pkgdir
 }
 
+// Windows installer
+
+func doWindowsInstaller(cmdline []string) {
+	// Parse the flags and make skip installer generation on PRs
+	var (
+		arch    = flag.String("arch", runtime.GOARCH, "Architecture for cross build packaging")
+		signer  = flag.String("signer", "", `Environment variable holding the signing key (e.g. WINDOWS_SIGNING_KEY)`)
+		upload  = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
+		workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
+	)
+	flag.CommandLine.Parse(cmdline)
+	*workdir = makeWorkdir(*workdir)
+	env := build.Env()
+	maybeSkipArchive(env)
+
+	// Aggregate binaries that are included in the installer
+	var (
+		devTools []string
+		allTools []string
+		gethTool string
+	)
+	for _, file := range allToolsArchiveFiles {
+		if file == "COPYING" { // license, copied later
+			continue
+		}
+		allTools = append(allTools, filepath.Base(file))
+		if filepath.Base(file) == "geth.exe" {
+			gethTool = file
+		} else {
+			devTools = append(devTools, file)
+		}
+	}
+
+	// Render NSIS scripts: Installer NSIS contains two installer sections,
+	// first section contains the geth binary, second section holds the dev tools.
+	templateData := map[string]interface{}{
+		"License":  "COPYING",
+		"Geth":     gethTool,
+		"DevTools": devTools,
+	}
+	build.Render("build/nsis.geth.nsi", filepath.Join(*workdir, "geth.nsi"), 0644, nil)
+	build.Render("build/nsis.install.nsh", filepath.Join(*workdir, "install.nsh"), 0644, templateData)
+	build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools)
+	build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil)
+	build.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll", 0755)
+	build.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING", 0755)
+
+	// Build the installer. This assumes that all the needed files have been previously
+	// built (don't mix building and packaging to keep cross compilation complexity to a
+	// minimum).
+	version := strings.Split(build.VERSION(), ".")
+	if env.Commit != "" {
+		version[2] += "-" + env.Commit[:8]
+	}
+	installer, _ := filepath.Abs("geth-" + archiveBasename(*arch, env) + ".exe")
+	build.MustRunCommand("makensis.exe",
+		"/DOUTPUTFILE="+installer,
+		"/DMAJORVERSION="+version[0],
+		"/DMINORVERSION="+version[1],
+		"/DBUILDVERSION="+version[2],
+		"/DARCH="+*arch,
+		filepath.Join(*workdir, "geth.nsi"),
+	)
+
+	// Sign and publish installer.
+	if err := archiveUpload(installer, *upload, *signer); err != nil {
+		log.Fatal(err)
+	}
+}
+
 // Cross compilation
 
 func doXgo(cmdline []string) {
diff --git a/build/nsis.envvarupdate.nsh b/build/nsis.envvarupdate.nsh
new file mode 100644
index 0000000000000000000000000000000000000000..9c3ecbe3372fb9696033498f0394bbb486af6d9e
--- /dev/null
+++ b/build/nsis.envvarupdate.nsh
@@ -0,0 +1,327 @@
+/**
+ *  EnvVarUpdate.nsh
+ *    : Environmental Variables: append, prepend, and remove entries
+ *
+ *     WARNING: If you use StrFunc.nsh header then include it before this file
+ *              with all required definitions. This is to avoid conflicts
+ *
+ *  Usage:
+ *    ${EnvVarUpdate} "ResultVar" "EnvVarName" "Action" "RegLoc" "PathString"
+ *
+ *  Credits:
+ *  Version 1.0
+ *  * Cal Turney (turnec2)
+ *  * Amir Szekely (KiCHiK) and e-circ for developing the forerunners of this
+ *    function: AddToPath, un.RemoveFromPath, AddToEnvVar, un.RemoveFromEnvVar,
+ *    WriteEnvStr, and un.DeleteEnvStr
+ *  * Diego Pedroso (deguix) for StrTok
+ *  * Kevin English (kenglish_hi) for StrContains
+ *  * Hendri Adriaens (Smile2Me), Diego Pedroso (deguix), and Dan Fuhry
+ *    (dandaman32) for StrReplace
+ *
+ *  Version 1.1 (compatibility with StrFunc.nsh)
+ *  * techtonik
+ *
+ *  http://nsis.sourceforge.net/Environmental_Variables:_append%2C_prepend%2C_and_remove_entries
+ *
+ */
+
+
+!ifndef ENVVARUPDATE_FUNCTION
+!define ENVVARUPDATE_FUNCTION
+!verbose push
+!verbose 3
+!include "LogicLib.nsh"
+!include "WinMessages.NSH"
+!include "StrFunc.nsh"
+
+; ---- Fix for conflict if StrFunc.nsh is already includes in main file -----------------------
+!macro _IncludeStrFunction StrFuncName
+  !ifndef ${StrFuncName}_INCLUDED
+    ${${StrFuncName}}
+  !endif
+  !ifndef Un${StrFuncName}_INCLUDED
+    ${Un${StrFuncName}}
+  !endif
+  !define un.${StrFuncName} "${Un${StrFuncName}}"
+!macroend
+
+!insertmacro _IncludeStrFunction StrTok
+!insertmacro _IncludeStrFunction StrStr
+!insertmacro _IncludeStrFunction StrRep
+
+; ---------------------------------- Macro Definitions ----------------------------------------
+!macro _EnvVarUpdateConstructor ResultVar EnvVarName Action Regloc PathString
+  Push "${EnvVarName}"
+  Push "${Action}"
+  Push "${RegLoc}"
+  Push "${PathString}"
+    Call EnvVarUpdate
+  Pop "${ResultVar}"
+!macroend
+!define EnvVarUpdate '!insertmacro "_EnvVarUpdateConstructor"'
+
+!macro _unEnvVarUpdateConstructor ResultVar EnvVarName Action Regloc PathString
+  Push "${EnvVarName}"
+  Push "${Action}"
+  Push "${RegLoc}"
+  Push "${PathString}"
+    Call un.EnvVarUpdate
+  Pop "${ResultVar}"
+!macroend
+!define un.EnvVarUpdate '!insertmacro "_unEnvVarUpdateConstructor"'
+; ---------------------------------- Macro Definitions end-------------------------------------
+
+;----------------------------------- EnvVarUpdate start----------------------------------------
+!define hklm_all_users     'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
+!define hkcu_current_user  'HKCU "Environment"'
+
+!macro EnvVarUpdate UN
+
+Function ${UN}EnvVarUpdate
+
+  Push $0
+  Exch 4
+  Exch $1
+  Exch 3
+  Exch $2
+  Exch 2
+  Exch $3
+  Exch
+  Exch $4
+  Push $5
+  Push $6
+  Push $7
+  Push $8
+  Push $9
+  Push $R0
+
+  /* After this point:
+  -------------------------
+     $0 = ResultVar     (returned)
+     $1 = EnvVarName    (input)
+     $2 = Action        (input)
+     $3 = RegLoc        (input)
+     $4 = PathString    (input)
+     $5 = Orig EnvVar   (read from registry)
+     $6 = Len of $0     (temp)
+     $7 = tempstr1      (temp)
+     $8 = Entry counter (temp)
+     $9 = tempstr2      (temp)
+     $R0 = tempChar     (temp)  */
+
+  ; Step 1:  Read contents of EnvVarName from RegLoc
+  ;
+  ; Check for empty EnvVarName
+  ${If} $1 == ""
+    SetErrors
+    DetailPrint "ERROR: EnvVarName is blank"
+    Goto EnvVarUpdate_Restore_Vars
+  ${EndIf}
+
+  ; Check for valid Action
+  ${If}    $2 != "A"
+  ${AndIf} $2 != "P"
+  ${AndIf} $2 != "R"
+    SetErrors
+    DetailPrint "ERROR: Invalid Action - must be A, P, or R"
+    Goto EnvVarUpdate_Restore_Vars
+  ${EndIf}
+
+  ${If} $3 == HKLM
+    ReadRegStr $5 ${hklm_all_users} $1     ; Get EnvVarName from all users into $5
+  ${ElseIf} $3 == HKCU
+    ReadRegStr $5 ${hkcu_current_user} $1  ; Read EnvVarName from current user into $5
+  ${Else}
+    SetErrors
+    DetailPrint 'ERROR: Action is [$3] but must be "HKLM" or HKCU"'
+    Goto EnvVarUpdate_Restore_Vars
+  ${EndIf}
+
+  ; Check for empty PathString
+  ${If} $4 == ""
+    SetErrors
+    DetailPrint "ERROR: PathString is blank"
+    Goto EnvVarUpdate_Restore_Vars
+  ${EndIf}
+
+  ; Make sure we've got some work to do
+  ${If} $5 == ""
+  ${AndIf} $2 == "R"
+    SetErrors
+    DetailPrint "$1 is empty - Nothing to remove"
+    Goto EnvVarUpdate_Restore_Vars
+  ${EndIf}
+
+  ; Step 2: Scrub EnvVar
+  ;
+  StrCpy $0 $5                             ; Copy the contents to $0
+  ; Remove spaces around semicolons (NOTE: spaces before the 1st entry or
+  ; after the last one are not removed here but instead in Step 3)
+  ${If} $0 != ""                           ; If EnvVar is not empty ...
+    ${Do}
+      ${${UN}StrStr} $7 $0 " ;"
+      ${If} $7 == ""
+        ${ExitDo}
+      ${EndIf}
+      ${${UN}StrRep} $0  $0 " ;" ";"         ; Remove '<space>;'
+    ${Loop}
+    ${Do}
+      ${${UN}StrStr} $7 $0 "; "
+      ${If} $7 == ""
+        ${ExitDo}
+      ${EndIf}
+      ${${UN}StrRep} $0  $0 "; " ";"         ; Remove ';<space>'
+    ${Loop}
+    ${Do}
+      ${${UN}StrStr} $7 $0 ";;"
+      ${If} $7 == ""
+        ${ExitDo}
+      ${EndIf}
+      ${${UN}StrRep} $0  $0 ";;" ";"
+    ${Loop}
+
+    ; Remove a leading or trailing semicolon from EnvVar
+    StrCpy  $7  $0 1 0
+    ${If} $7 == ";"
+      StrCpy $0  $0 "" 1                   ; Change ';<EnvVar>' to '<EnvVar>'
+    ${EndIf}
+    StrLen $6 $0
+    IntOp $6 $6 - 1
+    StrCpy $7  $0 1 $6
+    ${If} $7 == ";"
+     StrCpy $0  $0 $6                      ; Change ';<EnvVar>' to '<EnvVar>'
+    ${EndIf}
+    ; DetailPrint "Scrubbed $1: [$0]"      ; Uncomment to debug
+  ${EndIf}
+
+  /* Step 3. Remove all instances of the target path/string (even if "A" or "P")
+     $6 = bool flag (1 = found and removed PathString)
+     $7 = a string (e.g. path) delimited by semicolon(s)
+     $8 = entry counter starting at 0
+     $9 = copy of $0
+     $R0 = tempChar      */
+
+  ${If} $5 != ""                           ; If EnvVar is not empty ...
+    StrCpy $9 $0
+    StrCpy $0 ""
+    StrCpy $8 0
+    StrCpy $6 0
+
+    ${Do}
+      ${${UN}StrTok} $7 $9 ";" $8 "0"      ; $7 = next entry, $8 = entry counter
+
+      ${If} $7 == ""                       ; If we've run out of entries,
+        ${ExitDo}                          ;    were done
+      ${EndIf}                             ;
+
+      ; Remove leading and trailing spaces from this entry (critical step for Action=Remove)
+      ${Do}
+        StrCpy $R0  $7 1
+        ${If} $R0 != " "
+          ${ExitDo}
+        ${EndIf}
+        StrCpy $7   $7 "" 1                ;  Remove leading space
+      ${Loop}
+      ${Do}
+        StrCpy $R0  $7 1 -1
+        ${If} $R0 != " "
+          ${ExitDo}
+        ${EndIf}
+        StrCpy $7   $7 -1                  ;  Remove trailing space
+      ${Loop}
+      ${If} $7 == $4                       ; If string matches, remove it by not appending it
+        StrCpy $6 1                        ; Set 'found' flag
+      ${ElseIf} $7 != $4                   ; If string does NOT match
+      ${AndIf}  $0 == ""                   ;    and the 1st string being added to $0,
+        StrCpy $0 $7                       ;    copy it to $0 without a prepended semicolon
+      ${ElseIf} $7 != $4                   ; If string does NOT match
+      ${AndIf}  $0 != ""                   ;    and this is NOT the 1st string to be added to $0,
+        StrCpy $0 $0;$7                    ;    append path to $0 with a prepended semicolon
+      ${EndIf}                             ;
+
+      IntOp $8 $8 + 1                      ; Bump counter
+    ${Loop}                                ; Check for duplicates until we run out of paths
+  ${EndIf}
+
+  ; Step 4:  Perform the requested Action
+  ;
+  ${If} $2 != "R"                          ; If Append or Prepend
+    ${If} $6 == 1                          ; And if we found the target
+      DetailPrint "Target is already present in $1. It will be removed and"
+    ${EndIf}
+    ${If} $0 == ""                         ; If EnvVar is (now) empty
+      StrCpy $0 $4                         ;   just copy PathString to EnvVar
+      ${If} $6 == 0                        ; If found flag is either 0
+      ${OrIf} $6 == ""                     ; or blank (if EnvVarName is empty)
+        DetailPrint "$1 was empty and has been updated with the target"
+      ${EndIf}
+    ${ElseIf} $2 == "A"                    ;  If Append (and EnvVar is not empty),
+      StrCpy $0 $0;$4                      ;     append PathString
+      ${If} $6 == 1
+        DetailPrint "appended to $1"
+      ${Else}
+        DetailPrint "Target was appended to $1"
+      ${EndIf}
+    ${Else}                                ;  If Prepend (and EnvVar is not empty),
+      StrCpy $0 $4;$0                      ;     prepend PathString
+      ${If} $6 == 1
+        DetailPrint "prepended to $1"
+      ${Else}
+        DetailPrint "Target was prepended to $1"
+      ${EndIf}
+    ${EndIf}
+  ${Else}                                  ; If Action = Remove
+    ${If} $6 == 1                          ;   and we found the target
+      DetailPrint "Target was found and removed from $1"
+    ${Else}
+      DetailPrint "Target was NOT found in $1 (nothing to remove)"
+    ${EndIf}
+    ${If} $0 == ""
+      DetailPrint "$1 is now empty"
+    ${EndIf}
+  ${EndIf}
+
+  ; Step 5:  Update the registry at RegLoc with the updated EnvVar and announce the change
+  ;
+  ClearErrors
+  ${If} $3  == HKLM
+    WriteRegExpandStr ${hklm_all_users} $1 $0     ; Write it in all users section
+  ${ElseIf} $3 == HKCU
+    WriteRegExpandStr ${hkcu_current_user} $1 $0  ; Write it to current user section
+  ${EndIf}
+
+  IfErrors 0 +4
+    MessageBox MB_OK|MB_ICONEXCLAMATION "Could not write updated $1 to $3"
+    DetailPrint "Could not write updated $1 to $3"
+    Goto EnvVarUpdate_Restore_Vars
+
+  ; "Export" our change
+  SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
+
+  EnvVarUpdate_Restore_Vars:
+  ;
+  ; Restore the user's variables and return ResultVar
+  Pop $R0
+  Pop $9
+  Pop $8
+  Pop $7
+  Pop $6
+  Pop $5
+  Pop $4
+  Pop $3
+  Pop $2
+  Pop $1
+  Push $0  ; Push my $0 (ResultVar)
+  Exch
+  Pop $0   ; Restore his $0
+
+FunctionEnd
+
+!macroend   ; EnvVarUpdate UN
+!insertmacro EnvVarUpdate ""
+!insertmacro EnvVarUpdate "un."
+;----------------------------------- EnvVarUpdate end----------------------------------------
+
+!verbose pop
+!endif
\ No newline at end of file
diff --git a/build/nsis.geth.nsi b/build/nsis.geth.nsi
new file mode 100644
index 0000000000000000000000000000000000000000..dbeb9319c9c3c19c891617109315baee7d115557
--- /dev/null
+++ b/build/nsis.geth.nsi
@@ -0,0 +1,65 @@
+# Builds a Windows installer with NSIS.
+# It expects the following command line arguments:
+# - OUTPUTFILE, filename of the installer (without extension)
+# - MAJORVERSION, major build version
+# - MINORVERSION, minor build version
+# - BUILDVERSION, build id version
+#
+# The created installer executes the following steps:
+# 1. install geth for all users
+# 2. install optional development tools such as abigen
+# 3. create an uninstaller
+# 4. configures the Windows firewall for geth
+# 5. create geth, attach and uninstall start menu entries
+# 6. configures the registry that allows Windows to manage the package through its platform tools
+# 7. adds the environment system wide variable ETHEREUM_SOCKET
+# 8. adds the install directory to %PATH%
+#
+# Requirements:
+# - NSIS, http://nsis.sourceforge.net/Main_Page
+# - SFP, http://nsis.sourceforge.net/NSIS_Simple_Firewall_Plugin (put dll in NSIS\Plugins\x86-ansi)
+#
+# based on: http://nsis.sourceforge.net/A_simple_installer_with_start_menu_shortcut_and_uninstaller
+#
+# TODO:
+# - sign installer
+CRCCheck on
+
+!define GROUPNAME "Ethereum"
+!define APPNAME "Geth"
+!define DESCRIPTION "Official Go implementation of the Ethereum protocol"
+!addplugindir .\
+
+# Require admin rights on NT6+ (When UAC is turned on)
+RequestExecutionLevel admin
+
+# Use LZMA compression
+SetCompressor /SOLID lzma
+
+!include LogicLib.nsh
+!include EnvVarUpdate.nsh
+
+!macro VerifyUserIsAdmin
+UserInfo::GetAccountType
+pop $0
+${If} $0 != "admin" # Require admin rights on NT4+
+  messageBox mb_iconstop "Administrator rights required!"
+  setErrorLevel 740 # ERROR_ELEVATION_REQUIRED
+  quit
+${EndIf}
+!macroend
+
+function .onInit
+  # make vars are global for all users since geth is installed global
+  setShellVarContext all
+  !insertmacro VerifyUserIsAdmin
+
+  ${If} ${ARCH} == "amd64"
+    StrCpy $InstDir "$PROGRAMFILES64\${APPNAME}"
+  ${Else}
+    StrCpy $InstDir "$PROGRAMFILES32\${APPNAME}"
+  ${Endif}
+functionEnd
+
+!include install.nsh
+!include uninstall.nsh
diff --git a/build/nsis.install.nsh b/build/nsis.install.nsh
new file mode 100644
index 0000000000000000000000000000000000000000..f9ad8e95e1ec27f768428d39a08cbc2b2ebc21aa
--- /dev/null
+++ b/build/nsis.install.nsh
@@ -0,0 +1,102 @@
+Name "geth ${MAJORVERSION}.${MINORVERSION}.${BUILDVERSION}" # VERSION variables set through command line arguments
+InstallDir "$InstDir"
+OutFile "${OUTPUTFILE}" # set through command line arguments
+
+# Links for "Add/Remove Programs"
+!define HELPURL "https://github.com/ethereum/go-ethereum/issues"
+!define UPDATEURL "https://github.com/ethereum/go-ethereum/releases"
+!define ABOUTURL "https://github.com/ethereum/go-ethereum#ethereum-go"
+!define /date NOW "%Y%m%d"
+
+PageEx license
+  LicenseData {{.License}}
+PageExEnd
+
+# Install geth binary
+Section "Geth" GETH_IDX
+  SetOutPath $INSTDIR
+  file {{.Geth}}
+
+  # Create start menu launcher
+  createDirectory "$SMPROGRAMS\${APPNAME}"
+  createShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\geth.exe" "--fast" "--cache=512"
+  createShortCut "$SMPROGRAMS\${APPNAME}\Attach.lnk" "$INSTDIR\geth.exe" "attach" "" ""
+  createShortCut "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "" ""
+
+  # Firewall - remove rules (if exists)
+  SimpleFC::AdvRemoveRule "Geth incoming peers (TCP:30303)"
+  SimpleFC::AdvRemoveRule "Geth outgoing peers (TCP:30303)"
+  SimpleFC::AdvRemoveRule "Geth UDP discovery (UDP:30303)"
+
+  # Firewall - add rules
+  SimpleFC::AdvAddRule "Geth incoming peers (TCP:30303)" ""  6 1 1 2147483647 1 "$INSTDIR\geth.exe" "" "" "Ethereum" 30303 "" "" ""
+  SimpleFC::AdvAddRule "Geth outgoing peers (TCP:30303)" ""  6 2 1 2147483647 1 "$INSTDIR\geth.exe" "" "" "Ethereum" "" 30303 "" ""
+  SimpleFC::AdvAddRule "Geth UDP discovery (UDP:30303)" "" 17 2 1 2147483647 1 "$INSTDIR\geth.exe" "" "" "Ethereum" "" 30303 "" ""
+
+  # Set default IPC endpoint (https://github.com/ethereum/EIPs/issues/147)
+  ${EnvVarUpdate} $0 "ETHEREUM_SOCKET" "R" "HKLM" "\\.\pipe\geth.ipc"
+  ${EnvVarUpdate} $0 "ETHEREUM_SOCKET" "A" "HKLM" "\\.\pipe\geth.ipc"
+
+  # Add geth to PATH
+  ${EnvVarUpdate} $0 "PATH" "A" "HKLM" $INSTDIR
+SectionEnd
+
+# Install optional develop tools.
+Section /o "Development tools" DEV_TOOLS_IDX
+  SetOutPath $INSTDIR
+  {{range .DevTools}}file {{.}}
+  {{end}}
+SectionEnd
+
+# Return on top of stack the total size (as DWORD) of the selected/installed sections.
+Var GetInstalledSize.total
+Function GetInstalledSize
+  StrCpy $GetInstalledSize.total 0
+
+  ${if} ${SectionIsSelected} ${GETH_IDX}
+    SectionGetSize ${GETH_IDX} $0
+    IntOp $GetInstalledSize.total $GetInstalledSize.total + $0
+  ${endif}
+
+  ${if} ${SectionIsSelected} ${DEV_TOOLS_IDX}
+    SectionGetSize ${DEV_TOOLS_IDX} $0
+    IntOp $GetInstalledSize.total $GetInstalledSize.total + $0
+  ${endif}
+
+  IntFmt $GetInstalledSize.total "0x%08X" $GetInstalledSize.total
+  Push $GetInstalledSize.total
+FunctionEnd
+
+# Write registry, Windows uses these values in various tools such as add/remove program.
+# PowerShell: Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, InstallLocation, InstallDate | Format-Table –AutoSize
+function .onInstSuccess
+  # Save information in registry in HKEY_LOCAL_MACHINE branch, Windows add/remove functionality depends on this
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "DisplayName" "${GROUPNAME} - ${APPNAME} - ${DESCRIPTION}"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "InstallLocation" "$INSTDIR"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "InstallDate" "${NOW}"
+  # Wait for Alex
+  #WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\logo.ico$\""
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "Publisher" "${GROUPNAME}"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "HelpLink" "${HELPURL}"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "URLUpdateInfo" "${UPDATEURL}"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "URLInfoAbout" "${ABOUTURL}"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "DisplayVersion" "${MAJORVERSION}.${MINORVERSION}.${BUILDVERSION}"
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "VersionMajor" ${MAJORVERSION}
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "VersionMinor" ${MINORVERSION}
+  # There is no option for modifying or repairing the install
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "NoModify" 1
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "NoRepair" 1
+
+  Call GetInstalledSize
+  Pop $0
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}" "EstimatedSize" "$0"
+
+  # Create uninstaller
+  writeUninstaller "$INSTDIR\uninstall.exe"
+functionEnd
+
+Page components
+Page directory
+Page instfiles
diff --git a/build/nsis.simplefc.dll b/build/nsis.simplefc.dll
new file mode 100644
index 0000000000000000000000000000000000000000..73b7d9634deddc41966698106dfcf3950c3cfb65
Binary files /dev/null and b/build/nsis.simplefc.dll differ
diff --git a/build/nsis.simplefc.source.zip b/build/nsis.simplefc.source.zip
new file mode 100644
index 0000000000000000000000000000000000000000..d7476022ad9949b5e84585ade092c4e499f28677
Binary files /dev/null and b/build/nsis.simplefc.source.zip differ
diff --git a/build/nsis.uninstall.nsh b/build/nsis.uninstall.nsh
new file mode 100644
index 0000000000000000000000000000000000000000..ea7d5e2981e66ca6a78babe7055b5a1af5f34c86
--- /dev/null
+++ b/build/nsis.uninstall.nsh
@@ -0,0 +1,32 @@
+Section "Uninstall"
+  # uninstall for all users
+  setShellVarContext all
+
+  # Delete (optionally) installed files
+  {{range $}}Delete $INSTDIR\{{.}}
+  {{end}}
+  Delete $INSTDIR\uninstall.exe
+
+  # Delete install directory
+  rmDir $INSTDIR
+
+  # Delete start menu launcher
+  Delete "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk"
+  Delete "$SMPROGRAMS\${APPNAME}\Attach.lnk"
+  Delete "$SMPROGRAMS\${APPNAME}\Uninstall.lnk"
+  rmDir "$SMPROGRAMS\${APPNAME}"
+
+  # Firewall - remove rules if exists
+  SimpleFC::AdvRemoveRule "Geth incoming peers (TCP:30303)"
+  SimpleFC::AdvRemoveRule "Geth outgoing peers (TCP:30303)"
+  SimpleFC::AdvRemoveRule "Geth UDP discovery (UDP:30303)"
+
+  # Remove IPC endpoint (https://github.com/ethereum/EIPs/issues/147)
+  ${un.EnvVarUpdate} $0 "ETHEREUM_SOCKET" "R" "HKLM" "\\.\pipe\geth.ipc"
+
+  # Remove install directory from PATH
+  ${un.EnvVarUpdate} $0 "PATH" "R" "HKLM" $INSTDIR
+
+  # Cleanup registry (deletes all sub keys)
+  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}"
+SectionEnd
diff --git a/internal/build/util.go b/internal/build/util.go
index a821cd7f23208ceb2400e72e25776de83ebc954d..ce17ce220be03967d3b498a4e9af4fda81850e36 100644
--- a/internal/build/util.go
+++ b/internal/build/util.go
@@ -20,6 +20,7 @@ import (
 	"bytes"
 	"flag"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"log"
 	"os"
@@ -117,3 +118,25 @@ func render(tpl *template.Template, outputFile string, outputPerm os.FileMode, x
 		log.Fatal(err)
 	}
 }
+
+// CopyFile copies a file.
+func CopyFile(dst, src string, mode os.FileMode) {
+	if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
+		log.Fatal(err)
+	}
+	destFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer destFile.Close()
+
+	srcFile, err := os.Open(src)
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer srcFile.Close()
+
+	if _, err := io.Copy(destFile, srcFile); err != nil {
+		log.Fatal(err)
+	}
+}