VM Configuration
Configure filesystem size, timeouts, working directory, and more.
Root Filesystem Size
Control the size of the VM's root filesystem (default: 16000 MB):
import { freestyle, VmTemplate } from "freestyle-sandboxes";
const template = new VmTemplate({
rootfsSizeMb: 32000, // 32 GB
});
const { vm } = await freestyle.vms.create({ template });The filesystem uses ext4 and can be resized after creation:
await vm.resize({ sizeMb: 64000 }); // Resize to 64 GBNote: You can only increase the size, not decrease it.
Working Directory
Set the default directory for commands and file operations:
const { vm } = await freestyle.vms.create({
workdir: "/app",
gitRepos: [
{ repo: "https://github.com/owner/repo", path: "/app" },
],
});
// Commands run in /app by default
await vm.exec("npm install"); // Runs in /app
await vm.exec("ls"); // Lists /app contentsWithout a working directory, commands run in /root:
const { vm } = await freestyle.vms.create();
await vm.exec("pwd"); // Output: /rootIdle Timeout
Automatically suspend VMs after a period of network inactivity (default: 300 seconds):
const { vm } = await freestyle.vms.create({
idleTimeoutSeconds: 600, // 10 minutes
});Set to null to disable idle timeout:
const { vm } = await freestyle.vms.create({
idleTimeoutSeconds: null, // Never auto-suspend
});Cost consideration: VMs with no idle timeout continue running (and billing) indefinitely. Use persistent or sticky storage to preserve state while allowing suspension.
Ready Signals
Control when the VM creation API call returns by waiting for the VM to be fully ready:
const { vm } = await freestyle.vms.create({
waitForReadySignal: true,
readySignalTimeoutSeconds: 120, // Default: 120 seconds
});By default (waitForReadySignal: false), the API returns as soon as the serial console shows the login prompt. This typically takes 1-2 seconds.
When waitForReadySignal: true, the API waits for the serial console to reach the login prompt before returning. This ensures the VM is fully booted and ready to accept commands.
Note: In the future, this will be configurable to wait for custom readiness conditions like systemd service health checks or application-specific signals.
Recreate
Allow the VM to be recreated automatically if deleted:
const { vm, vmId } = await freestyle.vms.create({
recreate: true,
gitRepos: [
{ repo: "https://github.com/owner/repo", path: "/app" },
],
});
// Delete the VM
await freestyle.vms.delete({ vmId });
// The VM can be recreated with the same ID and configuration
const recreated = await vm.start(); // Automatically recreatesThis is useful for:
- Serverless-style workloads
- Cost optimization (delete when not in use)
- Automatic recovery from failures
Additional Files
Add files to the VM filesystem:
const { vm } = await freestyle.vms.create({
additionalFiles: {
"/app/config.json": {
content: JSON.stringify({ apiKey: "secret" }),
},
"/etc/app/settings.conf": {
content: "debug=true\nport=3000",
},
},
});Binary Files
Use base64 encoding for binary files:
additionalFiles: {
"/app/image.png": {
content: "iVBORw0KGgoAAAANSUhEUgA...", // Base64 encoded
encoding: "base64",
},
}Large Files
For large files, consider:
- Using git repositories
- Downloading during VM startup
- Using external storage
systemd: {
services: [
{
name: "download-assets",
mode: "oneshot",
exec: ["curl -o /app/data.zip https://example.com/data.zip"],
wantedBy: ["multi-user.target"],
},
],
}Template vs Direct Configuration
Most configuration options can be used in two ways:
In a Template (recommended for caching)
const template = new VmTemplate({
rootfsSizeMb: 32000,
workdir: "/app",
gitRepos: [
{ repo: "owner/repo", path: "/app" },
],
additionalFiles: {
"/app/.env": { content: "NODE_ENV=production" },
},
});
// Cached after first creation
const { vm } = await freestyle.vms.create({ template });Directly (applied on top of template/snapshot)
const { vm } = await freestyle.vms.create({
template, // Base configuration
// These are applied after the template
additionalFiles: {
"/app/custom.txt": { content: "custom content" },
},
workdir: "/custom", // Overrides template workdir
idleTimeoutSeconds: 600,
});Combining Options
A complete example combining multiple configuration options:
import { freestyle, VmTemplate } from "freestyle-sandboxes";
const template = new VmTemplate({
rootfsSizeMb: 32000,
workdir: "/app",
gitRepos: [
{
repo: "https://github.com/owner/webapp",
path: "/app",
rev: "main",
},
],
additionalFiles: {
"/app/.env.production": {
content: "DATABASE_URL=...\nAPI_KEY=...",
},
},
systemd: {
services: [
{
name: "install-deps",
mode: "oneshot",
exec: ["npm install"],
workdir: "/app",
after: ["freestyle-git-sync.service"],
wantedBy: ["multi-user.target"],
},
{
name: "web-server",
mode: "service",
exec: ["npm start"],
workdir: "/app",
after: ["install-deps.service"],
env: {
PORT: "3000",
NODE_ENV: "production",
},
},
],
},
});
const { vm, domains } = await freestyle.vms.create({
template,
ports: [
{ port: 443, targetPort: 3000 },
],
idleTimeoutSeconds: 600,
persistence: {
type: "ephemeral",
deleteEvent: "OnSuspend",
},
});
console.log(`App: https://${domains[0]}`);Best Practices
Use templates for static configuration
Put configuration that doesn't change between VMs in templates:
- Git repositories
- System dependencies
- Base files
- Systemd services
Keep dynamic config outside templates
Apply per-VM configuration directly:
- Secrets/API keys
- Environment-specific settings
- Port mappings
- Persistence settings
Example: Staging vs Production
const baseTemplate = new VmTemplate({
gitRepos: [{ repo: "https://github.com/owner/app", path: "/app" }],
systemd: { /* ... */ },
});
// Staging
const { vm: staging } = await freestyle.vms.create({
template: baseTemplate,
additionalFiles: {
"/app/.env": { content: "ENV=staging\nAPI_URL=..." },
},
idleTimeoutSeconds: 300, // Suspend quickly
});
// Production
const { vm: prod } = await freestyle.vms.create({
template: baseTemplate,
additionalFiles: {
"/app/.env": { content: "ENV=production\nAPI_URL=..." },
},
persistence: { type: "persistent" },
idleTimeoutSeconds: null, // Never suspend
});