Skip to content

Cross-Compilation

When you deploy a Node.js or TypeScript application to a server, you must ensure the target machine has:

  1. The correct version of Node.js installed
  2. All node_modules/ dependencies present (or bundled)
  3. The correct native addon binaries for the target OS/CPU (e.g., bcrypt, sharp)

Deploying to a different architecture — say, compiling on an x86_64 Mac and deploying to an ARM-based AWS Graviton server — requires either a matching local environment or a Docker build that targets the right platform.

Go cross-compilation in two environment variables

Section titled “Go cross-compilation in two environment variables”

Go has first-class support for cross-compilation. You set two environment variables before go build and the compiler produces a binary for the target platform — no Docker, no cross-compiler toolchain, no emulation layer required.

Terminal window
# The two magic variables
GOOS=<target-os> GOARCH=<target-arch>
# Common values
GOOS: linux darwin windows freebsd
GOARCH: amd64 arm64 386 arm
Terminal window
# Build for Linux AMD64 (most AWS EC2, typical server)
GOOS=linux GOARCH=amd64 go build -o bin/app-linux-amd64 ./cmd/app
# Build for Linux ARM64 (AWS Graviton, Apple Silicon EC2)
GOOS=linux GOARCH=arm64 go build -o bin/app-linux-arm64 ./cmd/app
# Build for Windows from macOS or Linux
GOOS=windows GOARCH=amd64 go build -o bin/app.exe ./cmd/app
# Build for macOS Apple Silicon from any platform
GOOS=darwin GOARCH=arm64 go build -o bin/app-darwin-arm64 ./cmd/app

Run these in your terminal. You will see the binary appear in bin/ immediately, cross-compiled from your host machine with no extra tools.

Release script: build for multiple targets

Section titled “Release script: build for multiple targets”

A simple shell script to produce release binaries for all common platforms:

#!/usr/bin/env bash
set -euo pipefail
APP="myapp"
VERSION=$(git describe --tags --always)
OUTDIR="dist/${VERSION}"
mkdir -p "${OUTDIR}"
targets=(
"linux/amd64"
"linux/arm64"
"darwin/amd64"
"darwin/arm64"
"windows/amd64"
)
for target in "${targets[@]}"; do
GOOS="${target%/*}"
GOARCH="${target#*/}"
ext=""
[[ "$GOOS" == "windows" ]] && ext=".exe"
output="${OUTDIR}/${APP}-${GOOS}-${GOARCH}${ext}"
echo "Building ${output}..."
CGO_ENABLED=0 GOOS="${GOOS}" GOARCH="${GOARCH}" \
go build -ldflags="-s -w -X main.version=${VERSION}" \
-o "${output}" ./cmd/${APP}
done
echo "Done. Binaries in ${OUTDIR}/"
TypeScript
# TypeScript/Node deployment checklist
# (target machine must have all of these)
1. Install Node.js v20 on the server
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash
sudo apt-get install -y nodejs
2. Upload your build artifact + package.json
scp -r dist/ package.json server:/app/
3. Install production deps on the server
ssh server "cd /app && npm ci --omit=dev"
4. Rebuild native addons for target platform
npm rebuild bcrypt sharp # if you use them
5. Start the process
node dist/main.js
Go
# Go cross-compilationbuild on dev, deploy anywhere
# Target machine needs: NOTHING. Just copy the binary.
# From your Mac, build for a Linux server:
GOOS=linux GOARCH=amd64 \
CGO_ENABLED=0 \
go build -o app-linux ./cmd/app
# Upload the single binary
scp app-linux server:/usr/local/bin/myapp
# On the serverjust run it:
ssh server "/usr/local/bin/myapp"
Which environment variable specifies the target operating system for a Go cross-compile?
What does `CGO_ENABLED=0` do in the context of cross-compilation?
After cross-compiling a Go binary for Linux, what does the target Linux server need installed to run it?
Which GOOS value would you use to build a Go binary that runs on Apple Silicon Macs?