Server Build
Wails v3 supports server mode, allowing you to run your application as a pure HTTP server without creating native windows or requiring GUI dependencies. This enables deploying the same Wails application to servers, containers, and web browsers.
Overview
Section titled “Overview”Server mode is useful for:
- Docker/Container deployments - Run without X11/Wayland dependencies
- Server-side applications - Deploy as a web server accessible via browser
- Web-only access - Share the same codebase between desktop and web
- CI/CD testing - Run integration tests without a display server
- Microservices - Use Wails bindings in headless backend services
Quick Start
Section titled “Quick Start”Server mode is enabled via the server build tag. Your application code remains the same - you just build with the tag:
# Using Taskfile (recommended)wails3 task build:serverwails3 task run:server
# Or build directly with Gogo build -tags server -o myapp-server .Here’s a minimal example:
package main
import ( "embed" "log"
"github.com/wailsapp/wails/v3/pkg/application")
//go:embed frontend/distvar assets embed.FS
func main() { app := application.New(application.Options{ Name: "My App", // Server options are used when built with -tags server Server: application.ServerOptions{ Host: "localhost", Port: 8080, }, Services: []application.Service{ application.NewService(&MyService{}), }, Assets: application.AssetOptions{ Handler: application.AssetFileServerFS(assets), }, })
log.Println("Starting application...") if err := app.Run(); err != nil { log.Fatal(err) }}The same code can be built for desktop (without the tag) or server mode (with -tags server).
Configuration
Section titled “Configuration”ServerOptions
Section titled “ServerOptions”Configure the HTTP server with ServerOptions:
Server: application.ServerOptions{ // Host to bind to. Default: "localhost" // Use "0.0.0.0" to listen on all interfaces Host: "localhost",
// Port to listen on. Default: 8080 Port: 8080,
// Request read timeout. Default: 30s ReadTimeout: 30 * time.Second,
// Response write timeout. Default: 30s WriteTimeout: 30 * time.Second,
// Idle connection timeout. Default: 120s IdleTimeout: 120 * time.Second,
// Graceful shutdown timeout. Default: 30s ShutdownTimeout: 30 * time.Second,
// TLS configuration (optional) TLS: &application.TLSOptions{ CertFile: "/path/to/cert.pem", KeyFile: "/path/to/key.pem", },},Features
Section titled “Features”Health Check Endpoint
Section titled “Health Check Endpoint”A health check endpoint is automatically available at /health:
curl http://localhost:8080/health# {"status":"ok"}This is useful for:
- Kubernetes liveness/readiness probes
- Load balancer health checks
- Monitoring systems
Service Bindings
Section titled “Service Bindings”All service bindings work identically to desktop mode:
type GreetService struct{}
func (g *GreetService) Greet(name string) string { return "Hello, " + name + "!"}
// Register in optionsServices: []application.Service{ application.NewService(&GreetService{}),},The frontend can call these bindings using the standard Wails runtime:
const greeting = await wails.Call.ByName('main.GreetService.Greet', 'World');Events
Section titled “Events”Events work bidirectionally in server mode:
- Frontend to Backend: Events emitted from the browser are sent via HTTP and received by your Go event handlers
- Backend to Frontend: Events emitted from Go are broadcast to all connected browsers via WebSocket
Each browser tab is represented as a “window” with a unique name (browser-1, browser-2, etc.), accessible via event.Sender:
// Listen for events from browsersapp.Event.On("user-action", func(event *application.CustomEvent) { log.Printf("Event from %s: %v", event.Sender, event.Data) // event.Sender will be "browser-1", "browser-2", etc.})
// Emit events to all connected browsersapp.Event.Emit("server-update", data)From the frontend:
// Emit event to server (and all other browsers)await wails.Events.Emit('user-action', { action: 'click' });
// Listen for events from serverwails.Events.On('server-update', (event) => { console.log('Update from server:', event.data);});Graceful Shutdown
Section titled “Graceful Shutdown”The server handles SIGINT and SIGTERM signals gracefully:
- Stops accepting new connections
- Waits for active requests to complete (up to
ShutdownTimeout) - Runs
OnShutdownhooks - Shuts down services in reverse order
Differences from Desktop Mode
Section titled “Differences from Desktop Mode”| Feature | Desktop Mode | Server Mode |
|---|---|---|
| Native windows | Created | Browser windows (browser-N) |
| System tray | Available | Not available |
| Native dialogs | Available | Not available |
| Application menu | Available | Not available |
| Screen info | Available | Returns error |
| Service bindings | Works | Works |
| Events | Works | Works (via WebSocket) |
| Assets | Via webview | Via HTTP |
| CGO required | Yes | No |
Window API Behavior
Section titled “Window API Behavior”In server mode, window-related APIs are safely handled:
app.Window.NewWithOptions()- Logs a warning, returns nilapp.Hide()/app.Show()- No-opapp.Screen.GetPrimary()- Returns error
This allows code that references windows to run without crashing, though window operations have no effect.
Building for Production
Section titled “Building for Production”Using Task (Recommended)
Section titled “Using Task (Recommended)”Projects created with wails3 init include a build:server task:
# Build for server modewails3 task build:server
# Build and runwails3 task run:serverManual Build
Section titled “Manual Build”# Build with server modego build -tags server -o myapp-server .Docker
Section titled “Docker”Wails projects include a ready-to-use Docker setup. To build and run your application in a container:
# Build the Docker imagewails3 task build:docker
# Run itwails3 task run:dockerThat’s it! Your application will be available at http://localhost:8080.
You can customise the build with a few options:
# Use a custom image tagwails3 task build:docker TAG=myapp:v1.0.0
# Run on a different portwails3 task run:docker PORT=3000The generated Dockerfile.server creates a minimal image based on distroless. It handles the network binding automatically, so your application will be accessible from outside the container.
Docker Compose
Section titled “Docker Compose”For more complex deployments, here’s a Docker Compose configuration with health checks:
services: app: build: . ports: - "8080:8080" environment: - WAILS_SERVER_HOST=0.0.0.0 healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3Custom Dockerfile
Section titled “Custom Dockerfile”If you need more control, you can create your own Dockerfile. The key thing to remember is setting WAILS_SERVER_HOST=0.0.0.0 so the server accepts connections from outside the container:
# Build stageFROM golang:alpine AS builderWORKDIR /appRUN apk add --no-cache gitCOPY . .RUN go mod tidyRUN go build -tags server -ldflags="-s -w" -o server .
# Runtime stageFROM gcr.io/distroless/static-debian12COPY --from=builder /app/server /serverCOPY --from=builder /app/frontend/dist /frontend/distEXPOSE 8080ENV WAILS_SERVER_HOST=0.0.0.0ENTRYPOINT ["/server"]Security Considerations
Section titled “Security Considerations”When deploying server mode applications:
- Bind to localhost by default - Only use
0.0.0.0when needed - Use TLS in production - Configure
ServerOptions.TLS - Place behind reverse proxy - Use nginx/traefik for additional security
- Validate all inputs - Same security practices as any web application
Example
Section titled “Example”A complete example is available at v3/examples/server/:
cd v3/examples/server
# Using Taskfiletask dev
# Or run directlygo run -tags server .
# Open http://localhost:8080 in browserEnvironment Variables
Section titled “Environment Variables”For deployment scenarios where you need to override the server configuration without changing code, Wails recognises these environment variables:
| Variable | Description | Default |
|---|---|---|
WAILS_SERVER_HOST | Network interface to bind to | localhost |
WAILS_SERVER_PORT | Port to listen on | 8080 |
These take precedence over the ServerOptions in your code, which is why the Docker examples set WAILS_SERVER_HOST=0.0.0.0 - it allows the container to accept external connections without requiring any changes to your application.
See Also
Section titled “See Also”- Custom Transport - For advanced IPC customization
- Services - Service binding documentation
- Events - Event system documentation