# Go on Windows: one binary, one service, one proxy

> Deploy a Go HTTP server as a Windows service, pass config by environment, handle graceful shutdown, and reverse-proxy it safely.

**Tested on**

- Build: 26100.33650
- Edition: Standard / Datacenter
- Architecture: x64
- Evidence: E1
- Verified: 2026-10-29

## The short answer

A Go server is a single `.exe`. There is no runtime to install on the server:

```powershell
go build -trimpath -ldflags="-s -w" -o C:\apps\api\api.exe .\cmd\api
```

Run it as a Windows service with WinSW/NSSM, and let IIS/Nginx own 443.

## Build for the target

```powershell
$env:GOOS='windows'; $env:GOARCH='amd64'
go build -trimpath -ldflags="-s -w" -o C:\build\api.exe .\cmd\api
```

Build on the target architecture; do not ship a binary built for another OS/arch. `-trimpath` removes local paths; `-s -w` strips symbols (keep a separate debug build if you need stack traces).

## Service wrapper

WinSW:

```xml
<service>
  <id>api</id>
  <executable>C:\apps\api\api.exe</executable>
  <workingdirectory>C:\apps\api</workingdirectory>
  <env name="APP_ENV" value="production"/>
  <env name="LISTEN_ADDR" value="127.0.0.1:8080"/>
  <logpath>C:\logs\api</logpath>
  <onfailure action="restart" delay="10 sec"/>
</service>
```

The app should read `LISTEN_ADDR`/config from environment or a file next to the exe. Do not hard-code production secrets in the binary.

## Graceful shutdown

Handle `SIGTERM`/console close and call `server.Shutdown(ctx)` with a timeout. Windows service stop sends a control event; a Go app that ignores it gets killed after the service timeout and may drop in-flight requests.

## Reverse proxy

```nginx
location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}
```

The Go app should trust forwarded headers only from the proxy. Bind to localhost; do not expose 8080.

## Verify

```powershell
sc.exe qc api
Get-CimInstance Win32_Process -Filter "Name='api.exe'" |
  Select-Object ProcessId, ExecutablePath, CommandLine
curl.exe -I http://127.0.0.1:8080/healthz
Get-Content C:\logs\api\api.err.log -Tail 50
```

Check the binary path, health endpoint and logs. A 502 from the proxy usually means the service is not listening.

> **Caution: Single binary does not mean no operations**
>
> You still need service supervision, logs, config/secrets, health checks, patching and a rollback binary. Go removes the runtime, not the operational work.

## Lab boundary

E1. The lab did not build or run a Go service; the commands are the deployment pattern to validate on Server 2025.

## Sources

- [Go — Download and install](https://go.dev/doc/install)
- [WinSW](https://github.com/winsw/winsw)
- [Go — net/http Server](https://pkg.go.dev/net/http#Server)

---

Source: https://winbook.fyi/en/server/runtimes/go-service/
