File System Operations
Daytona provides comprehensive file system operations through the fs module in sandboxes.
Basic operations
Section titled “Basic operations”Daytona provides methods to interact with the file system in sandboxes. You can perform various operations like listing files, creating directories, reading and writing files, and more.
File operations assume you are operating in the sandbox user’s home directory (e.g. workspace implies /home/[username]/workspace). Use a leading / when providing absolute paths.
List files and directories
Section titled “List files and directories”List files and directories in a sandbox by providing the path to the directory.
# List files in a directoryfiles = sandbox.fs.list_files("workspace")
for file in files: print(f"Name: {file.name}") print(f"Is directory: {file.is_dir}") print(f"Size: {file.size}") print(f"Modified: {file.mod_time}")// List files in a directoryconst files = await sandbox.fs.listFiles('workspace')
files.forEach(file => { console.log(`Name: ${file.name}`) console.log(`Is directory: ${file.isDir}`) console.log(`Size: ${file.size}`) console.log(`Modified: ${file.modTime}`)})# List directory contentsfiles = sandbox.fs.list_files("workspace/data")
# Print files and their sizesfiles.each do |file| puts "#{file.name}: #{file.size} bytes" unless file.is_dirend
# List only directoriesdirs = files.select(&:is_dir)puts "Subdirectories: #{dirs.map(&:name).join(', ')}"// List files in a directoryfiles, err := sandbox.FileSystem.ListFiles(ctx, "workspace")if err != nil { log.Fatal(err)}
for _, file := range files { fmt.Printf("Name: %s\n", file.Name) fmt.Printf("Is directory: %t\n", file.IsDirectory) fmt.Printf("Size: %d\n", file.Size) fmt.Printf("Modified: %s\n", file.ModifiedTime)}import io.daytona.sdk.model.FileInfo;import java.util.List;
List<FileInfo> files = sandbox.fs.listFiles("workspace");for (FileInfo file : files) { System.out.println("Name: " + file.getName()); System.out.println("Is directory: " + file.getIsDir()); System.out.println("Size: " + file.getSize()); System.out.println("Modified: " + file.getModTime());}# List one directory level (default depth=1)curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files?path=workspace'
# List recursively: depth=2 includes immediate childrencurl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files?path=workspace&depth=2'Get directory or file information
Section titled “Get directory or file information”Get directory or file information by providing the path to the directory or file.
# Get file metadatainfo = sandbox.fs.get_file_info("workspace/data/file.txt")print(f"Size: {info.size} bytes")print(f"Modified: {info.mod_time}")print(f"Mode: {info.mode}")
# Check if path is a directoryinfo = sandbox.fs.get_file_info("workspace/data")if info.is_dir: print("Path is a directory")// Get file detailsconst info = await sandbox.fs.getFileDetails('app/config.json')console.log(`Size: ${info.size}, Modified: ${info.modifiedAt ?? info.modTime}`)# Get file metadatainfo = sandbox.fs.get_file_info("workspace/data/file.txt")puts "Size: #{info.size} bytes"puts "Modified: #{info.mod_time}"puts "Mode: #{info.mode}"
# Check if path is a directoryinfo = sandbox.fs.get_file_info("workspace/data")puts "Path is a directory" if info.is_dir// Get file metadatainfo, err := sandbox.FileSystem.GetFileInfo(ctx, "workspace/data/file.txt")if err != nil { log.Fatal(err)}fmt.Printf("Size: %d bytes\n", info.Size)fmt.Printf("Modified: %s\n", info.ModifiedTime)fmt.Printf("Mode: %s\n", info.Mode)
// Check if path is a directoryinfo, err = sandbox.FileSystem.GetFileInfo(ctx, "workspace/data")if err != nil { log.Fatal(err)}if info.IsDirectory { fmt.Println("Path is a directory")}import io.daytona.sdk.model.FileInfo;
FileInfo info = sandbox.fs.getFileDetails("workspace/data/file.txt");System.out.println("Size: " + info.getSize() + " bytes");System.out.println("Modified: " + info.getModTime());System.out.println("Mode: " + info.getMode());
info = sandbox.fs.getFileDetails("workspace/data");if (Boolean.TRUE.equals(info.getIsDir())) { System.out.println("Path is a directory");}curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/info?path='Create directories
Section titled “Create directories”Create a directory by providing the path and permissions to set on the directory.
# Create with specific permissionssandbox.fs.create_folder("workspace/new-dir", "755")// Create with specific permissionsawait sandbox.fs.createFolder('workspace/new-dir', '755')# Create a directory with standard permissionssandbox.fs.create_folder("workspace/data", "755")
# Create a private directorysandbox.fs.create_folder("workspace/secrets", "700")// Create with specific permissionserr := sandbox.FileSystem.CreateFolder(ctx, "workspace/new-dir", options.WithMode("755"),)if err != nil { log.Fatal(err)}sandbox.fs.createFolder("workspace/new-dir", "755");curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/folder?path=&mode=' \ --request POSTUpload files
Section titled “Upload files”Daytona provides methods to upload a single or multiple files in sandboxes.
Upload a single file
Section titled “Upload a single file”Upload a single file by providing the content to upload and the path to the file to upload it to.
# Upload from memory (small files)content = b"Hello, World!"sandbox.fs.upload_file(content, "remote_file.txt")
# Upload from a local file path (streams large files)sandbox.fs.upload_file("local_file.txt", "remote_file.txt")// Upload a single fileconst fileContent = Buffer.from('Hello, World!')await sandbox.fs.uploadFile(fileContent, 'data.txt')# Upload a text file from string contentcontent = "Hello, World!"sandbox.fs.upload_file(content, "tmp/hello.txt")
# Upload a local filesandbox.fs.upload_file("local_file.txt", "tmp/file.txt")
# Upload binary datadata = { key: "value" }.to_jsonsandbox.fs.upload_file(data, "tmp/config.json")// Upload from a local file patherr := sandbox.FileSystem.UploadFile(ctx, "local_file.txt", "remote_file.txt")if err != nil { log.Fatal(err)}
// Or upload from byte contentcontent := []byte("Hello, World!")err = sandbox.FileSystem.UploadFile(ctx, content, "hello.txt")if err != nil { log.Fatal(err)}import java.nio.charset.StandardCharsets;
byte[] fileContent = "Hello, World!".getBytes(StandardCharsets.UTF_8);sandbox.fs.uploadFile(fileContent, "data.txt");curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/upload?path=' \ --request POST \ --header 'Content-Type: multipart/form-data' \ --form 'file='Upload multiple files
Section titled “Upload multiple files”Upload multiple files by providing the content to upload and their destination paths.
# Upload multiple files at oncefiles_to_upload = []
with open("file1.txt", "rb") as f1: files_to_upload.append(FileUpload( source=f1.read(), destination="data/file1.txt", ))
with open("file2.txt", "rb") as f2: files_to_upload.append(FileUpload( source=f2.read(), destination="data/file2.txt", ))
with open("settings.json", "rb") as f3: files_to_upload.append(FileUpload( source=f3.read(), destination="config/settings.json", ))
sandbox.fs.upload_files(files_to_upload)// Upload multiple files at onceconst files = [ { source: Buffer.from('Content of file 1'), destination: 'data/file1.txt', }, { source: Buffer.from('Content of file 2'), destination: 'data/file2.txt', }, { source: Buffer.from('{"key": "value"}'), destination: 'config/settings.json', },]
await sandbox.fs.uploadFiles(files)# Upload multiple filesfiles = [ FileUpload.new("Content of file 1", "/tmp/file1.txt"), FileUpload.new("workspace/data/file2.txt", "/tmp/file2.txt"), FileUpload.new('{"key": "value"}', "/tmp/config.json")]
sandbox.fs.upload_files(files)// Upload multiple files by calling UploadFile for eachfilesToUpload := []struct { source string destination string}{ {"file1.txt", "data/file1.txt"}, {"file2.txt", "data/file2.txt"}, {"settings.json", "config/settings.json"},}
for _, f := range filesToUpload { err := sandbox.FileSystem.UploadFile(ctx, f.source, f.destination) if err != nil { log.Fatal(err) }}import java.nio.charset.StandardCharsets;
sandbox.fs.uploadFile("Hello, World!".getBytes(StandardCharsets.UTF_8), "data/file1.txt");sandbox.fs.uploadFile("More content".getBytes(StandardCharsets.UTF_8), "data/file2.txt");curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/bulk-upload' \ --request POSTStream uploads
Section titled “Stream uploads”For large files, use streaming upload methods to avoid loading the entire file into memory.
with open("large_dataset.csv", "rb") as f: sandbox.fs.upload_file_stream(f, "workspace/dataset.csv")import { createReadStream } from 'node:fs'
await sandbox.fs.uploadFileStream( createReadStream('large_dataset.csv'), 'workspace/dataset.csv')File.open("large_dataset.csv", "rb") do |f| sandbox.fs.upload_file_stream(f, "workspace/dataset.csv")endf, err := os.Open("large_dataset.csv")if err != nil { log.Fatal(err)}defer f.Close()
err = sandbox.FileSystem.UploadFileStream(ctx, f, "workspace/dataset.csv")if err != nil { log.Fatal(err)}import java.io.FileInputStream;import java.nio.file.Path;
try (var in = new FileInputStream(Path.of("large_dataset.csv").toFile())) { sandbox.fs.uploadFileStream(in, "workspace/dataset.csv");}Download files
Section titled “Download files”Daytona provides methods to download files from sandboxes.
Download a single file
Section titled “Download a single file”Download a single file by providing the path to the file to download.
from daytona import DaytonaNotFoundError
try: content = sandbox.fs.download_file("file1.txt")except DaytonaNotFoundError as error: print(f"Missing file: {error}")else: with open("local_file.txt", "wb") as f: f.write(content)
print(content.decode("utf-8"))
# Stream a large file to disk without loading it into memorysandbox.fs.download_file("workspace/large-file.bin", "local_copy.bin")import { DaytonaNotFoundError } from '@daytona/sdk'
try { const downloadedFile = await sandbox.fs.downloadFile('file1.txt') console.log('File content:', downloadedFile.toString())} catch (error) { if (error instanceof DaytonaNotFoundError) { console.error(`Missing file: ${error.message}`) } else { throw error }}# Download and get file contentcontent = sandbox.fs.download_file("workspace/data/file.txt")puts content
# Download and save a file locallysandbox.fs.download_file("workspace/data/file.txt", "local_copy.txt")size_mb = File.size("local_copy.txt") / 1024.0 / 1024.0puts "Size of the downloaded file: #{size_mb} MB"// Download and get contents in memorycontent, err := sandbox.FileSystem.DownloadFile(ctx, "file1.txt", nil)if err != nil { log.Fatal(err)}fmt.Println(string(content))
// Download and save to a local filelocalPath := "local_file.txt"content, err = sandbox.FileSystem.DownloadFile(ctx, "file1.txt", &localPath)if err != nil { log.Fatal(err)}import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.nio.file.Path;
byte[] content = sandbox.fs.downloadFile("file1.txt");System.out.println(new String(content, StandardCharsets.UTF_8));
Files.write(Path.of("local_file.txt"), content);curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/download?path='Download multiple files
Section titled “Download multiple files”Download multiple files by providing the paths to the files to download.
# Download multiple files at oncefiles_to_download = [ FileDownloadRequest(source="data/file1.txt"), # No destination - download to memory FileDownloadRequest(source="data/file2.txt", destination="local_file2.txt"), # Download to local file]
results = sandbox.fs.download_files(files_to_download)
for result in results: if result.error: print(f"Error downloading {result.source}: {result.error}") if result.error_details: print( f" status={result.error_details.status_code} " f"code={result.error_details.error_code}" ) elif result.result: print(f"Downloaded {result.source} to {result.result}")// Download multiple files at onceconst files = [ { source: 'data/file1.txt' }, // No destination - download to memory { source: 'data/file2.txt', destination: 'local_file2.txt' }, // Download to local file]
const results = await sandbox.fs.downloadFiles(files)
results.forEach(result => { if (result.error) { console.error(`Error downloading ${result.source}: ${result.error}`) if (result.errorDetails) { console.error( ` status=${result.errorDetails.statusCode} code=${result.errorDetails.errorCode}` ) } } else if (result.result) { console.log(`Downloaded ${result.source} to ${result.result}`) }})# Download multiple files by calling download_file for eachfiles_to_download = [ { remote: "data/file1.txt", local: nil }, # Download to memory { remote: "data/file2.txt", local: "local_file2.txt" } # Download to local file]
files_to_download.each do |f| if f[:local] sandbox.fs.download_file(f[:remote], f[:local]) puts "Downloaded #{f[:remote]} to #{f[:local]}" else content = sandbox.fs.download_file(f[:remote]) puts "Downloaded #{f[:remote]} to memory (#{content.size} bytes)" endend// Download multiple files by calling DownloadFile for eachfilesToDownload := []struct { remotePath string localPath *string}{ {"data/file1.txt", nil}, // Download to memory {"data/file2.txt", ptrString("local_file2.txt")}, // Download to local file}
for _, f := range filesToDownload { content, err := sandbox.FileSystem.DownloadFile(ctx, f.remotePath, f.localPath) if err != nil { fmt.Printf("Error downloading %s: %v\n", f.remotePath, err) continue } if f.localPath == nil { fmt.Printf("Downloaded %s to memory (%d bytes)\n", f.remotePath, len(content)) } else { fmt.Printf("Downloaded %s to %s\n", f.remotePath, *f.localPath) }}curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/bulk-download' \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "paths": [ "" ]}'Stream downloads
Section titled “Stream downloads”For large files, use streaming download methods to avoid loading the entire file into memory.
with open("local_copy.bin", "wb") as f: for chunk in sandbox.fs.download_file_stream("workspace/large-file.bin"): f.write(chunk)import { createWriteStream } from 'node:fs'import { pipeline } from 'node:stream/promises'
const stream = await sandbox.fs.downloadFileStream('workspace/large-file.bin')await pipeline(stream, createWriteStream('local_copy.bin'))File.open("local_copy.bin", "wb") do |f| sandbox.fs.download_file_stream("workspace/large-file.bin") { |chunk| f.write(chunk) }endstream, err := sandbox.FileSystem.DownloadFileStream(ctx, "workspace/large-file.bin")if err != nil { log.Fatal(err)}defer stream.Close()
out, err := os.Create("local_copy.bin")if err != nil { log.Fatal(err)}defer out.Close()
_, err = io.Copy(out, stream)if err != nil { log.Fatal(err)}import java.io.FileOutputStream;import java.io.InputStream;import java.nio.file.Path;
try (InputStream in = sandbox.fs.downloadFileStream("workspace/large-file.bin"); var out = new FileOutputStream(Path.of("local_copy.bin").toFile())) { in.transferTo(out);}Delete files
Section titled “Delete files”Delete a file or directory by providing the path to the file or directory to delete.
Pass recursive: true to delete a directory recursively.
sandbox.fs.delete_file("workspace/file.txt")
# Delete a directory recursivelysandbox.fs.delete_file("workspace/old_dir", recursive=True)await sandbox.fs.deleteFile('workspace/file.txt')
// Delete a directory recursivelyawait sandbox.fs.deleteFile('workspace/old_dir', true)# Delete a filesandbox.fs.delete_file("workspace/data/old_file.txt")
# Delete a directory recursivelysandbox.fs.delete_file("workspace/old_dir", recursive: true)// Delete a fileerr := sandbox.FileSystem.DeleteFile(ctx, "workspace/file.txt", false)if err != nil { log.Fatal(err)}
// Delete a directory recursivelyerr = sandbox.FileSystem.DeleteFile(ctx, "workspace/old_dir", true)if err != nil { log.Fatal(err)}sandbox.fs.deleteFile("workspace/file.txt");# Delete a filecurl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files?path=workspace/file.txt' \ --request DELETE
# Delete a directory recursivelycurl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files?path=workspace/old_dir&recursive=true' \ --request DELETEAdvanced operations
Section titled “Advanced operations”Daytona provides advanced file system operations such as file permissions, search by file name, content search and replace, and move files.
File permissions
Section titled “File permissions”Set file permissions, ownership, and group for a file or directory by providing the path to the file or directory and the permissions to set.
# Set file permissionssandbox.fs.set_file_permissions("workspace/file.txt", "644")
# Get file permissionsfile_info = sandbox.fs.get_file_info("workspace/file.txt")print(f"Permissions: {file_info.permissions}")// Set file permissionsawait sandbox.fs.setFilePermissions('workspace/file.txt', { mode: '644' })
// Get file permissionsconst fileInfo = await sandbox.fs.getFileDetails('workspace/file.txt')console.log(`Permissions: ${fileInfo.permissions}`)# Make a file executablesandbox.fs.set_file_permissions( path: "workspace/scripts/run.sh", mode: "755" # rwxr-xr-x)
# Change file ownersandbox.fs.set_file_permissions( path: "workspace/data/file.txt", owner: "daytona", group: "daytona")// Set file permissionserr := sandbox.FileSystem.SetFilePermissions(ctx, "workspace/file.txt", options.WithPermissionMode("644"),)if err != nil { log.Fatal(err)}
// Set owner and grouperr = sandbox.FileSystem.SetFilePermissions(ctx, "workspace/file.txt", options.WithOwner("daytona"), options.WithGroup("daytona"),)if err != nil { log.Fatal(err)}
// Get file info to check permissionsfileInfo, err := sandbox.FileSystem.GetFileInfo(ctx, "workspace/file.txt")if err != nil { log.Fatal(err)}fmt.Printf("Mode: %s\n", fileInfo.Mode)curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/permissions?path=' \ --request POSTSearch files by pattern
Section titled “Search files by pattern”Search for files and directories by name using glob patterns (for example *.py). This is distinct from find_files / findFiles, which searches file contents.
result = sandbox.fs.search_files("workspace", "*.py")for file in result.files: print(file)const result = await sandbox.fs.searchFiles('workspace', '*.ts')result.files.forEach(file => console.log(file))result = sandbox.fs.search_files("workspace", "*.rb")result.files.each { |file| puts file }result, err := sandbox.FileSystem.SearchFiles(ctx, "workspace", "*.go")if err != nil { log.Fatal(err)}files := result.(map[string]any)["files"].([]string)for _, file := range files { fmt.Println(file)}import java.util.List;import java.util.Map;
Map<String, Object> result = sandbox.fs.searchFiles("workspace", "*.java");@SuppressWarnings("unchecked")List<String> files = (List<String>) result.get("files");for (String file : files) { System.out.println(file);}curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/search?path=workspace&pattern=*.py'Find and replace text in files
Section titled “Find and replace text in files”Find and replace text in files by providing the path to the directory to search in and the pattern to search for.
# Search for text in files by providing the path to the directory to search in and the pattern to search forresults = sandbox.fs.find_files( path="workspace/src", pattern="text-of-interest")for match in results: print(f"Absolute file path: {match.file}") print(f"Line number: {match.line}") print(f"Line content: {match.content}") print("\n")
# Replace text in filessandbox.fs.replace_in_files( files=["workspace/file1.txt", "workspace/file2.txt"], pattern="old_text", new_value="new_text")// Search for text in files; if a folder is specified, the search is recursiveconst results = await sandbox.fs.findFiles('workspace/src', 'text-of-interest')results.forEach(match => { console.log('Absolute file path:', match.file) console.log('Line number:', match.line) console.log('Line content:', match.content)})
// Replace text in filesawait sandbox.fs.replaceInFiles( ["workspace/file1.txt", "workspace/file2.txt"], "old_text", "new_text")# Search for TODOs in Ruby filesmatches = sandbox.fs.find_files("workspace/src", "TODO:")matches.each do |match| puts "#{match.file}:#{match.line}: #{match.content.strip}"end
# Replace in specific filesresults = sandbox.fs.replace_in_files( files: ["workspace/src/file1.rb", "workspace/src/file2.rb"], pattern: "old_function", new_value: "new_function")
# Print resultsresults.each do |result| if result.success puts "#{result.file}: #{result.success}" else puts "#{result.file}: #{result.error}" endend// Search for text in filesresult, err := sandbox.FileSystem.FindFiles(ctx, "workspace/src", "text-of-interest")if err != nil { log.Fatal(err)}matches := result.([]map[string]any)for _, match := range matches { fmt.Printf("Absolute file path: %s\n", match["file"]) fmt.Printf("Line number: %v\n", match["line"]) fmt.Printf("Line content: %s\n\n", match["content"])}
// Replace text in files_, err = sandbox.FileSystem.ReplaceInFiles(ctx, []string{"workspace/file1.txt", "workspace/file2.txt"}, "old_text", "new_text",)if err != nil { log.Fatal(err)}import java.util.Arrays;import java.util.List;import java.util.Map;
List<Map<String, Object>> results = sandbox.fs.findFiles("workspace/src", "text-of-interest");for (Map<String, Object> match : results) { System.out.println("Absolute file path: " + match.get("file")); System.out.println("Line number: " + match.get("line")); System.out.println("Line content: " + match.get("content")); System.out.println();}
sandbox.fs.replaceInFiles( Arrays.asList("workspace/file1.txt", "workspace/file2.txt"), "old_text", "new_text");Find text in files:
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/find?path=&pattern='Replace text in files:
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/replace' \ --request POST \ --header 'Content-Type: application/json' \ --data '{ "files": [ "" ], "newValue": "", "pattern": ""}'Move or rename directory or file
Section titled “Move or rename directory or file”Move or rename a directory or file by providing the path to the file or directory (source) and the new path to the file or directory (destination).
# Rename a filesandbox.fs.move_files( "workspace/data/old_name.txt", "workspace/data/new_name.txt")
# Move a file to a different directorysandbox.fs.move_files( "workspace/data/file.txt", "workspace/archive/file.txt")
# Move a directorysandbox.fs.move_files( "workspace/old_dir", "workspace/new_dir")// Move a file to a new locationawait sandbox.fs.moveFiles('app/temp/data.json', 'app/data/data.json')# Rename a filesandbox.fs.move_files( "workspace/data/old_name.txt", "workspace/data/new_name.txt")
# Move a file to a different directorysandbox.fs.move_files( "workspace/data/file.txt", "workspace/archive/file.txt")
# Move a directorysandbox.fs.move_files( "workspace/old_dir", "workspace/new_dir")// Rename a fileerr := sandbox.FileSystem.MoveFiles(ctx, "workspace/data/old_name.txt", "workspace/data/new_name.txt")if err != nil { log.Fatal(err)}
// Move a file to a different directoryerr = sandbox.FileSystem.MoveFiles(ctx, "workspace/data/file.txt", "workspace/archive/file.txt")if err != nil { log.Fatal(err)}
// Move a directoryerr = sandbox.FileSystem.MoveFiles(ctx, "workspace/old_dir", "workspace/new_dir")if err != nil { log.Fatal(err)}sandbox.fs.moveFiles("workspace/data/old_name.txt", "workspace/data/new_name.txt");
sandbox.fs.moveFiles("workspace/data/file.txt", "workspace/archive/file.txt");
sandbox.fs.moveFiles("workspace/old_dir", "workspace/new_dir");curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/move?source=&destination=' \ --request POST