Copying a file in Go means reading bytes from a source path and writing them to a destination path, then matching permission bits (and optionally ownership, which requires OS-specific APIs not covered here). os.Stat on the source yields fs.FileInfo; use info.Mode() with os.Chmod on the destination after data is written. For remote copies, tools and articles on SSH/rsync complement in-process Go (copy over SSH).
Tested with Go 1.24 on Linux.
Copy with io.Copy and os.Chmod
package main
import (
"fmt"
"io"
"os"
)
func main() {
const srcPath = "source_file.txt"
const dstPath = "/tmp/destination_file.txt"
src, err := os.Open(srcPath)
if err != nil {
panic(err)
}
defer src.Close()
info, err := src.Stat()
if err != nil {
panic(err)
}
dst, err := os.Create(dstPath)
if err != nil {
panic(err)
}
defer dst.Close()
if _, err := io.Copy(dst, src); err != nil {
panic(err)
}
dst.Close()
if err := os.Chmod(dstPath, info.Mode()); err != nil {
panic(err)
}
di, _ := os.Stat(dstPath)
fmt.Println("dst mode:", di.Mode())
}Run locally with a real source_file.txt. The critical call is io.Copy(dst, src) (writer first, reader second).
Copy with os.ReadFile / os.WriteFile
os.WriteFile(path, data, perm) sets mode on create; pass the source Mode().Perm() or full Mode() as appropriate:
package main
import (
"os"
)
func main() {
data, err := os.ReadFile("source_file.txt")
if err != nil {
panic(err)
}
info, err := os.Stat("source_file.txt")
if err != nil {
panic(err)
}
if err := os.WriteFile("/tmp/destination_file.txt", data, info.Mode().Perm()); err != nil {
panic(err)
}
}About os.Rename
os.Rename moves a path within the same filesystem; it is not a general-purpose copy and will not duplicate bytes across devices. After a rename, metadata usually travels with the inode; still verify modes if you split copy and metadata steps.
Summary
To preserve permissions when copying a file in Go, copy bytes with io.Copy (destination writer first, source reader second: io.Copy(dst, src)) or use os.WriteFile with the source FileMode, then align the destination mode with os.Chmod if needed. Do not treat os.Rename as a portable copy across devices. Prefer os.ReadFile over deprecated ioutil helpers.

