Skip to main content
Writing files in Go follows similar patterns to reading files. This guide covers different approaches from simple one-shot writes to granular buffered writing.

Basic File Writing

The simplest way to write data to a file is using os.WriteFile:
package main

import (
	"os"
	"path/filepath"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
	// Write bytes to file in one operation
	d1 := []byte("hello\ngo\n")
	path1 := filepath.Join(os.TempDir(), "dat1")
	err := os.WriteFile(path1, d1, 0644)
	check(err)
}
The third argument to os.WriteFile is the file permissions (0644 means read/write for owner, read-only for others).

Granular Writing with os.Create

For more control over writing operations, create a file to obtain an os.File value:
import "fmt"

// Create file for writing
path2 := filepath.Join(os.TempDir(), "dat2")
f, err := os.Create(path2)
check(err)

// Defer close immediately after opening
defer f.Close()
Always use defer f.Close() immediately after creating or opening a file to ensure proper cleanup.

Writing Byte Slices

You can write byte slices directly:
// Write byte slice
d2 := []byte{115, 111, 109, 101, 10} // "some\n"
n2, err := f.Write(d2)
check(err)
fmt.Printf("wrote %d bytes\n", n2)

Writing Strings

The WriteString method provides a convenient way to write strings:
// Write string directly
n3, err := f.WriteString("writes\n")
check(err)
fmt.Printf("wrote %d bytes\n", n3)
WriteString is a convenience method that converts the string to bytes internally before writing.

Syncing to Disk

Use Sync to flush writes to stable storage:
// Ensure all writes are flushed to disk
f.Sync()
Call Sync() when you need to ensure data is written to disk immediately, such as before critical operations or shutdown.

Buffered Writing

The bufio package provides buffered writers for improved performance:
import "bufio"

// Create buffered writer
w := bufio.NewWriter(f)

// Write to buffer
n4, err := w.WriteString("buffered\n")
check(err)
fmt.Printf("wrote %d bytes\n", n4)

// Flush buffer to underlying writer
w.Flush()
Always call Flush() on buffered writers to ensure all buffered data is written to the file.

Writing Patterns

One-Shot Write

Use os.WriteFile for writing complete data at once

Incremental Write

Use os.Create with Write for writing data in parts

String Writing

Use WriteString for convenient string operations

Buffered Writing

Use bufio.NewWriter for many small writes

Complete Example

package main

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
	// Simple write
	d1 := []byte("hello\ngo\n")
	path1 := filepath.Join(os.TempDir(), "dat1")
	err := os.WriteFile(path1, d1, 0644)
	check(err)

	// Granular writes
	path2 := filepath.Join(os.TempDir(), "dat2")
	f, err := os.Create(path2)
	check(err)
	defer f.Close()

	// Write bytes
	d2 := []byte{115, 111, 109, 101, 10}
	n2, err := f.Write(d2)
	check(err)
	fmt.Printf("wrote %d bytes\n", n2)

	// Write string
	n3, err := f.WriteString("writes\n")
	check(err)
	fmt.Printf("wrote %d bytes\n", n3)

	// Sync to disk
	f.Sync()

	// Buffered writing
	w := bufio.NewWriter(f)
	n4, err := w.WriteString("buffered\n")
	check(err)
	fmt.Printf("wrote %d bytes\n", n4)

	// Flush buffer
	w.Flush()
}

Best Practices

Use 0644 for regular files (owner read/write, others read-only) or 0600 for sensitive data (owner read/write only).
Use defer f.Close() immediately after creating/opening to ensure files are closed properly.
Check errors from all write operations. Disk full, permissions, and other issues can cause writes to fail.
Always call Flush() on buffered writers before closing files to ensure all data is written.
Call Sync() for critical data that must be persisted immediately to disk.
When making many small writes, use bufio.NewWriter to reduce system calls and improve performance.

Reading Files

Learn how to read data from files

Temporary Files

Working with temporary files and directories

Build docs developers (and LLMs) love