Skip to content

Build System

Wails provides a unified build system that compiles Go code, bundles frontend assets, embeds everything into a single executable, and handles platform-specific builds—all with one command.

Terminal window
wails3 build

Output: Native executable with everything embedded.

[Build Process Diagram Placeholder]

Wails scans your Go code to understand your services:

type GreetService struct {
prefix string
}
func (g *GreetService) Greet(name string) string {
return g.prefix + name + "!"
}

What Wails extracts:

  • Service name: GreetService
  • Method name: Greet
  • Parameter types: string
  • Return types: string

Used for: Generating TypeScript bindings

Wails generates type-safe bindings:

frontend/bindings/GreetService.ts
export function Greet(name: string): Promise<string> {
return window.wails.Call('GreetService.Greet', name)
}

Benefits:

  • Full type safety
  • IDE autocomplete
  • Compile-time errors
  • JSDoc comments

Your frontend bundler runs (Vite, webpack, etc.):

Terminal window
# Vite example
vite build --outDir dist

What happens:

  • JavaScript/TypeScript compiled
  • CSS processed and minified
  • Assets optimised
  • Source maps generated (dev only)
  • Output to frontend/dist/

Go code is compiled with optimisations:

Terminal window
go build -ldflags="-s -w" -o myapp.exe

Flags:

  • -s: Strip symbol table
  • -w: Strip DWARF debugging info
  • Result: Smaller binary (~30% reduction)

Platform-specific:

  • Windows: .exe with icon embedded
  • macOS: .app bundle structure
  • Linux: ELF binary

Frontend assets are embedded into the Go binary:

//go:embed frontend/dist
var assets embed.FS

Result: Single executable with everything inside.

Single native binary:

  • Windows: myapp.exe (~15MB)
  • macOS: myapp.app (~15MB)
  • Linux: myapp (~15MB)

No dependencies (except system WebView).

Optimised for speed:

Terminal window
wails3 dev

What happens:

  1. Starts frontend dev server (Vite on port 5173)
  2. Compiles Go without optimisations
  3. Launches app pointing to dev server
  4. Enables hot reload
  5. Includes source maps

Characteristics:

  • Fast rebuilds (<1s for frontend changes)
  • No asset embedding (served from dev server)
  • Debug symbols included
  • Source maps enabled
  • Verbose logging

File size: Larger (~50MB with debug symbols)

Terminal window
wails3 build

Output: build/bin/myapp[.exe]

Terminal window
# Build for Windows (from any OS)
wails3 build -platform windows/amd64
# Build for macOS
wails3 build -platform darwin/amd64
wails3 build -platform darwin/arm64
# Build for Linux
wails3 build -platform linux/amd64

Cross-compilation: Build for any platform from any platform.

Terminal window
# Custom output directory
wails3 build -o ./dist/myapp
# Skip frontend build (use existing)
wails3 build -skipbindings
# Clean build (remove cache)
wails3 build -clean
# Verbose output
wails3 build -v
Terminal window
# Debug build (includes symbols)
wails3 build -debug
# Production build (default, optimised)
wails3 build
# Development build (fast, unoptimised)
wails3 build -devbuild

Wails uses Taskfile for build configuration:

Taskfile.yml
version: '3'
tasks:
build:
desc: Build the application
cmds:
- wails3 build
build:windows:
desc: Build for Windows
cmds:
- wails3 build -platform windows/amd64
build:macos:
desc: Build for macOS (Universal)
cmds:
- wails3 build -platform darwin/amd64
- wails3 build -platform darwin/arm64
- lipo -create -output build/bin/myapp.app build/bin/myapp-amd64.app build/bin/myapp-arm64.app
build:linux:
desc: Build for Linux
cmds:
- wails3 build -platform linux/amd64

Run tasks:

Terminal window
task build:windows
task build:macos
task build:linux

Create build/build.json for persistent configuration:

{
"name": "My Application",
"version": "1.0.0",
"author": "Your Name",
"description": "Application description",
"icon": "build/appicon.png",
"outputFilename": "myapp",
"platforms": ["windows/amd64", "darwin/amd64", "linux/amd64"],
"frontend": {
"dir": "./frontend",
"install": "npm install",
"build": "npm run build",
"dev": "npm run dev"
},
"go": {
"ldflags": "-s -w -X main.version={{.Version}}"
}
}

Wails uses Go’s embed package:

package main
import (
"embed"
"github.com/wailsapp/wails/v3/pkg/application"
)
//go:embed frontend/dist
var assets embed.FS
func main() {
app := application.New(application.Options{
Name: "My App",
Assets: application.AssetOptions{
Handler: application.AssetFileServerFS(assets),
},
})
app.Window.New()
app.Run()
}

At build time:

  1. Frontend built to frontend/dist/
  2. //go:embed directive includes files
  3. Files compiled into binary
  4. Binary contains everything

At runtime:

  1. App starts
  2. Assets served from memory
  3. No disk I/O for assets
  4. Fast loading

Embed additional files:

//go:embed frontend/dist
var frontendAssets embed.FS
//go:embed data/*.json
var dataAssets embed.FS
//go:embed templates/*.html
var templateAssets embed.FS

Vite (default):

vite.config.js
export default {
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // Remove console.log
drop_debugger: true,
},
},
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'], // Separate vendor bundle
},
},
},
},
}

Results:

  • JavaScript minified (~70% reduction)
  • CSS minified (~60% reduction)
  • Images optimised
  • Tree-shaking applied

Compiler flags:

Terminal window
-ldflags="-s -w"
  • -s: Strip symbol table (~10% reduction)
  • -w: Strip DWARF debug info (~20% reduction)

Additional optimisations:

Terminal window
-ldflags="-s -w -X main.version=1.0.0"
  • -X: Set variable values at build time
  • Useful for version numbers, build dates

UPX (optional):

Terminal window
# After building
upx --best build/bin/myapp.exe

Results:

  • ~50% size reduction
  • Slightly slower startup (~100ms)
  • Not recommended for macOS (code signing issues)

Output: myapp.exe

Includes:

  • Application icon
  • Version information
  • Manifest (UAC settings)

Icon:

Terminal window
# Specify icon
wails3 build -icon build/appicon.png

Wails converts PNG to .ico automatically.

Manifest:

build/windows/manifest.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" name="MyApp"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

Output: myapp.app (application bundle)

Structure:

myapp.app/
├── Contents/
│ ├── Info.plist # App metadata
│ ├── MacOS/
│ │ └── myapp # Binary
│ ├── Resources/
│ │ └── icon.icns # Icon
│ └── _CodeSignature/ # Code signature (if signed)

Info.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>My App</string>
<key>CFBundleIdentifier</key>
<string>com.example.myapp</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
</dict>
</plist>

Universal Binary:

Terminal window
# Build for both architectures
wails3 build -platform darwin/amd64
wails3 build -platform darwin/arm64
# Combine into universal binary
lipo -create -output myapp-universal \
build/bin/myapp-amd64 \
build/bin/myapp-arm64

Output: myapp (ELF binary)

Dependencies:

  • GTK3
  • WebKitGTK

Desktop file:

# myapp.desktop
[Desktop Entry]
Name=My App
Exec=/usr/bin/myapp
Icon=myapp
Type=Application
Categories=Utility;

Installation:

Terminal window
# Copy binary
sudo cp myapp /usr/bin/
# Copy desktop file
sudo cp myapp.desktop /usr/share/applications/
# Copy icon
sudo cp icon.png /usr/share/icons/hicolor/256x256/apps/myapp.png
PhaseTimeNotes
Analysis<1sGo code scanning
Binding Generation<1sTypeScript generation
Frontend Build5-30sDepends on project size
Go Compilation2-10sDepends on code size
Asset Embedding<1sEmbedding frontend
Total10-45sFirst build
Incremental5-15sSubsequent builds

1. Use build cache:

Terminal window
# Go build cache is automatic
# Frontend cache (Vite)
npm run build # Uses cache by default

2. Skip unchanged steps:

Terminal window
# Skip frontend if unchanged
wails3 build -skipbindings

3. Parallel builds:

Terminal window
# Build multiple platforms in parallel
wails3 build -platform windows/amd64 &
wails3 build -platform darwin/amd64 &
wails3 build -platform linux/amd64 &
wait

4. Use faster tools:

Terminal window
# Use esbuild instead of webpack
# (Vite uses esbuild by default)

Symptom: wails3 build exits with error

Common causes:

  1. Go compilation error

    Terminal window
    # Check Go code compiles
    go build
  2. Frontend build error

    Terminal window
    # Check frontend builds
    cd frontend
    npm run build
  3. Missing dependencies

    Terminal window
    # Install dependencies
    npm install
    go mod download

Symptom: Binary is >50MB

Solutions:

  1. Strip debug symbols (should be automatic)

    Terminal window
    wails3 build # Already includes -ldflags="-s -w"
  2. Check embedded assets

    Terminal window
    # Remove unnecessary files from frontend/dist/
    # Check for large images, videos, etc.
  3. Use UPX compression

    Terminal window
    upx --best build/bin/myapp.exe

Symptom: Builds take >1 minute

Solutions:

  1. Use build cache

    • Go cache is automatic
    • Frontend cache (Vite) is automatic
  2. Skip unchanged steps

    Terminal window
    wails3 build -skipbindings
  3. Optimise frontend build

    vite.config.js
    export default {
    build: {
    minify: 'esbuild', // Faster than terser
    },
    }
  • Use wails3 dev during development - Fast iteration
  • Use wails3 build for releases - Optimised output
  • Version your builds - Use -ldflags to embed version
  • Test builds on target platforms - Cross-compilation isn’t perfect
  • Keep frontend builds fast - Optimise bundler config
  • Use build cache - Speeds up subsequent builds
  • Don’t commit build/ directory - Add to .gitignore
  • Don’t skip testing builds - Always test before release
  • Don’t embed unnecessary assets - Keep binaries small
  • Don’t use debug builds for production - Use optimised builds
  • Don’t forget code signing - Required for distribution

Building Applications - Detailed guide to building and packaging
Learn More →

Cross-Platform Builds - Build for all platforms from one machine
Learn More →

Creating Installers - Create installers for end users
Learn More →


Questions about building? Ask in Discord or check the build examples.