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 SSHDevelopment 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
-
Check the service is running:
await vm.exec("systemctl status my-service"); -
Verify it's listening on the right port:
await vm.exec("ss -tlnp | grep :3000"); -
Test internal access:
await vm.exec("curl http://localhost:3000"); -
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",
}