LogoFreestyle

Ports and Networking

Expose services running in your VMs to the internet.

Overview

Freestyle automatically provides HTTPS endpoints for services running in your VMs. By default, port 3000 in your VM is exposed on port 443 (HTTPS) with automatic SSL/TLS termination.

Default Behavior

Without any configuration, your VM gets:

  • Port 3000 (internal) → Port 443 (external, HTTPS)
  • Automatic SSL/TLS certificate
  • A unique domain like vm-abc123.freestyle.run
import { freestyle } from "freestyle-sandboxes";

const { vm, domains } = await freestyle.vms.create();

// Start a web server on port 3000
await vm.exec("python3 -m http.server 3000");

// Access it at domains[0]
console.log(`Visit: https://${domains[0]}`);

Custom Port Mapping

Map any internal port to external ports 443 or 8081:

const { vm, domains } = await freestyle.vms.create({
  ports: [
    {
      port: 443,        // External (HTTPS)
      targetPort: 8080, // Internal
    },
  ],
});

// Start server on port 8080
await vm.exec("node server.js"); // Listens on 8080

// Access at domains[0]:443 (or just domains[0])
console.log(`Visit: https://${domains[0]}`);

Multiple Ports

Expose multiple services from the same VM:

const { vm, domains } = await freestyle.vms.create({
  ports: [
    {
      port: 443,        // Main app
      targetPort: 3000,
    },
    {
      port: 8081,       // Admin panel
      targetPort: 4000,
    },
  ],
});

console.log(`App: https://${domains[0]}`);
console.log(`Admin: https://${domains[0]}:8081`);

No External Ports

Disable external access by passing an empty array:

const { vm } = await freestyle.vms.create({
  ports: [], // No external ports
});

// VM is only accessible via SDK/SSH
// Useful for background workers, data processing, etc.

Available External Ports

Currently, you can expose services on:

  • Port 443 (HTTPS) - Standard web traffic
  • Port 8081 (HTTPS) - Additional service

Any internal port can be mapped to these external ports.

Domain Access

Each VM gets one or more domains:

const { vm, domains } = await freestyle.vms.create({
  ports: [
    { port: 443, targetPort: 3000 },
    { port: 8081, targetPort: 4000 },
  ],
});

// All domains point to the same VM
console.log(domains); // ['vm-abc123.freestyle.run']

// Access different services via ports
// https://vm-abc123.freestyle.run (port 443)
// https://vm-abc123.freestyle.run:8081 (port 8081)

SSL/TLS Certificates

All external ports automatically get:

  • Valid SSL/TLS certificates
  • Automatic certificate renewal
  • HTTPS enforcement

No configuration needed!

Common Patterns

Web application

const { vm, domains } = await freestyle.vms.create({
  ports: [
    { port: 443, targetPort: 3000 },
  ],
  gitRepos: [
    { repo: "https://github.com/owner/webapp", path: "/app" },
  ],
  systemd: {
    services: [
      {
        name: "web-server",
        mode: "service",
        exec: ["npm start"],
        workdir: "/app",
        env: {
          PORT: "3000",
        },
      },
    ],
  },
});

console.log(`App running at https://${domains[0]}`);

API + Admin panel

const { vm, domains } = await freestyle.vms.create({
  ports: [
    { port: 443, targetPort: 3000 },  // Public API
    { port: 8081, targetPort: 4000 }, // Admin
  ],
  systemd: {
    services: [
      {
        name: "api",
        mode: "service",
        exec: ["node api.js"],
        env: { PORT: "3000" },
      },
      {
        name: "admin",
        mode: "service",
        exec: ["node admin.js"],
        env: { PORT: "4000" },
      },
    ],
  },
});

console.log(`API: https://${domains[0]}`);
console.log(`Admin: https://${domains[0]}:8081`);

Background worker (no external access)

const { vm } = await freestyle.vms.create({
  ports: [], // No external ports
  systemd: {
    services: [
      {
        name: "worker",
        mode: "service",
        exec: ["python worker.py"],
        env: {
          QUEUE_URL: "redis://...",
        },
      },
    ],
  },
});

// Only accessible via vm.exec() and SSH

Development server

const { vm, domains } = await freestyle.vms.create({
  ports: [
    { port: 443, targetPort: 5173 }, // Vite default
  ],
  gitRepos: [
    { repo: "owner/frontend", path: "/app" },
  ],
  systemd: {
    services: [
      {
        name: "dev-server",
        mode: "service",
        exec: ["npm run dev", "--", "--host", "0.0.0.0"],
        workdir: "/app",
        env: {
          PORT: "5173",
        },
      },
    ],
  },
});

console.log(`Dev server: https://${domains[0]}`);

Internal Networking

Services within the VM can communicate freely on any port:

const { vm } = await freestyle.vms.create({
  ports: [
    { port: 443, targetPort: 3000 }, // Only expose frontend
  ],
  systemd: {
    services: [
      {
        name: "database",
        mode: "service",
        exec: ["redis-server"],
        // Listens on 6379 (not exposed externally)
      },
      {
        name: "api",
        mode: "service",
        exec: ["node server.js"],
        env: {
          PORT: "3000",
          REDIS_URL: "redis://localhost:6379", // Internal access
        },
      },
    ],
  },
});

Port Configuration in Templates

Include port configuration in templates for consistent caching:

import { freestyle, VmTemplate } from "freestyle-sandboxes";

const webTemplate = new VmTemplate({
  ports: [
    { port: 443, targetPort: 3000 },
  ],
  systemd: {
    services: [
      {
        name: "web",
        mode: "service",
        exec: ["npm start"],
        workdir: "/app",
      },
    ],
  },
});

const { vm, domains } = await freestyle.vms.create({
  template: webTemplate,
  gitRepos: [
    { repo: "owner/app", path: "/app" },
  ],
});

Checking Open Ports

List what's listening in your VM:

// Check listening ports
await vm.exec("ss -tlnp");

// Check specific port
await vm.exec("lsof -i :3000");

// Test internal connectivity
await vm.exec("curl http://localhost:3000");

Troubleshooting

Service not accessible

  1. Check the service is running:

    await vm.exec("systemctl status my-service");
  2. Verify it's listening on the right port:

    await vm.exec("ss -tlnp | grep :3000");
  3. Test internal access:

    await vm.exec("curl http://localhost:3000");
  4. Check port mapping:

    const { vm, domains } = await freestyle.vms.create({
      ports: [
        { port: 443, targetPort: 3000 }, // Make sure this matches
      ],
    });

Wrong port exposed

Make sure your application listens on 0.0.0.0, not just localhost:

// ❌ Won't work
exec: ["node server.js"] // If it only binds to 127.0.0.1

// ✅ Works
exec: ["node server.js", "--host", "0.0.0.0"]

Or configure it via environment:

env: {
  HOST: "0.0.0.0",
  PORT: "3000",
}

On this page

Freestyle AI

Documentation assistant

Experimental: AI responses may not always be accurate—please verify important details with the official documentation.

How can I help?

Ask me about Freestyle while you browse the docs.