Claude Code for Fly.io: Deploy Anywhere with Anycast — Claude Skills 360 Blog
Blog / AI / Claude Code for Fly.io: Deploy Anywhere with Anycast
AI

Claude Code for Fly.io: Deploy Anywhere with Anycast

Published: July 20, 2027
Read time: 5 min read
By: Claude Skills 360

Fly.io runs Dockerized apps globally on anycast infrastructure — fly launch scaffolds a fly.toml and Dockerfile. fly deploy builds and deploys with zero-downtime rolling updates. fly.toml configures app, primary_region, [build], [[services]] (ports, [[services.tcp_checks]]), [env], and [[mounts]] for volumes. Machines API: POST https://api.machines.dev/v1/apps/{app}/machines starts ephemeral VMs with a custom Docker image in any region — useful for running AI jobs or preview environments. fly volumes create data --size 10 --region iad creates a persistent disk. fly postgres create provisions a managed Postgres cluster; fly postgres attach adds DATABASE_URL to app secrets. Private networking: every Fly app gets APP_NAME.internal DNS on the fdaa::/8 IPv6 network — services talk to each other without public internet. Secrets: fly secrets set KEY=VALUE injects env vars, fly secrets import < .env bulk loads. fly scale count 3 adds replicas; fly scale vm performance-2x upgrades hardware. fly logs streams live logs. fly ssh console opens a shell. Claude Code generates Fly.io fly.toml, Dockerfiles, Machines API integrations, and GitHub Actions CI/CD pipelines.

CLAUDE.md for Fly.io

## Fly.io Stack
- CLI: fly (Flyctl) — all operations via fly commands
- Init: fly launch — creates fly.toml + Dockerfile scaffold
- Deploy: fly deploy — builds Docker image, pushes, rolling restart
- Secrets: fly secrets set KEY=VALUE (or fly secrets import < .env for bulk)
- Scale: fly scale count 2 (replicas), fly scale vm shared-cpu-2x (size)
- Volumes: fly volumes create vol-name --size 10 --region iad; mount in [[mounts]] in fly.toml
- Postgres: fly postgres create --name myapp-db --region iad; fly postgres attach --app myapp
- Logs: fly logs --app myapp
- SSH: fly ssh console --app myapp

fly.toml Configuration

# fly.toml — production Next.js app on Fly.io
app             = "my-nextjs-app"
primary_region  = "iad"

[build]
dockerfile = "Dockerfile"

[env]
NODE_ENV  = "production"
PORT      = "3000"
# Sensitive values go in: fly secrets set OPENAI_API_KEY=sk-...

[[services]]
internal_port = 3000
protocol      = "tcp"

[[services.ports]]
port     = 80
handlers = ["http"]
force_https = true

[[services.ports]]
port     = 443
handlers = ["tls", "http"]

[services.concurrency]
type       = "connections"
hard_limit = 200
soft_limit = 150

[[services.http_checks]]
interval       = "10s"
timeout        = "5s"
grace_period   = "30s"
method         = "GET"
path           = "/api/health"

[[mounts]]
source      = "app_data"
destination = "/data"

Dockerfile for Next.js

# Dockerfile — optimized multi-stage Next.js build for Fly.io
FROM node:22-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

RUN addgroup --system nodejs && adduser --system --ingroup nodejs nextjs

COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

HEALTHCHECK --interval=10s --timeout=5s --start-period=30s \
  CMD wget -qO- http://localhost:3000/api/health || exit 1

CMD ["node", "server.js"]

Fly Machines API Client

// lib/fly/machines.ts — Fly Machines API for ephemeral GPU/CPU VMs
const FLY_API_TOKEN = process.env.FLY_API_TOKEN!
const FLY_APP_NAME  = process.env.FLY_APP_NAME!
const MACHINES_API  = "https://api.machines.dev/v1"

export type MachineConfig = {
  image:   string      // Docker image
  region?: string      // e.g. "iad", "lhr", "nrt"
  cpus?:   number
  memory?: number      // MB
  env?:    Record<string, string>
  cmd?:    string[]
}

export type Machine = {
  id:       string
  state:    "created" | "started" | "stopping" | "stopped" | "destroying" | "destroyed"
  region:   string
  image_ref: { registry: string; repository: string; tag: string }
}

async function flyFetch(path: string, init: RequestInit = {}): Promise<Response> {
  return fetch(`${MACHINES_API}${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${FLY_API_TOKEN}`,
      "Content-Type": "application/json",
      ...((init.headers as Record<string, string>) ?? {}),
    },
  })
}

/** Start a new Machine (ephemeral VM) */
export async function createMachine(config: MachineConfig): Promise<Machine> {
  const res = await flyFetch(`/apps/${FLY_APP_NAME}/machines`, {
    method: "POST",
    body: JSON.stringify({
      region: config.region ?? "iad",
      config: {
        image: config.image,
        guest: {
          cpus:        config.cpus ?? 1,
          memory_mb:   config.memory ?? 256,
          cpu_kind:    "shared",
        },
        env:  config.env ?? {},
        ...(config.cmd ? { cmd: config.cmd } : {}),
        auto_destroy: true, // destroy when process exits
        restart: { policy: "no" },
      },
    }),
  })
  if (!res.ok) throw new Error(`Create machine failed: ${await res.text()}`)
  return res.json()
}

/** Wait for a Machine to reach a state */
export async function waitForMachine(
  machineId: string,
  state: "started" | "stopped" | "destroyed",
  timeoutMs = 60_000,
): Promise<void> {
  const deadline = Date.now() + timeoutMs
  while (Date.now() < deadline) {
    const res = await flyFetch(`/apps/${FLY_APP_NAME}/machines/${machineId}`)
    const machine: Machine = await res.json()
    if (machine.state === state) return
    await new Promise((r) => setTimeout(r, 2000))
  }
  throw new Error(`Machine ${machineId} did not reach state "${state}" in ${timeoutMs}ms`)
}

/** Stop and destroy a Machine */
export async function destroyMachine(machineId: string): Promise<void> {
  await flyFetch(`/apps/${FLY_APP_NAME}/machines/${machineId}/stop`, { method: "POST" })
  await waitForMachine(machineId, "stopped")
  await flyFetch(`/apps/${FLY_APP_NAME}/machines/${machineId}`, { method: "DELETE" })
}

/** List running machines */
export async function listMachines(): Promise<Machine[]> {
  const res = await flyFetch(`/apps/${FLY_APP_NAME}/machines`)
  if (!res.ok) throw new Error(`List machines failed: ${await res.text()}`)
  return res.json()
}

GitHub Actions CI/CD

# .github/workflows/deploy.yml — automated Fly.io deploy on push
name: Deploy to Fly.io

on:
  push:
    branches: [main]

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    concurrency: deploy-group  # prevent simultaneous deploys
    steps:
      - uses: actions/checkout@v4

      - uses: superfly/flyctl-actions/setup-flyctl@master

      - name: Deploy
        run: fly deploy --remote-only
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

For the Railway alternative when needing a simpler UI-driven platform with automatic Postgres provisioning, deploy previews on PR, and a more beginner-friendly experience — Railway abstracts away more infrastructure while Fly.io gives full Dockerfile control, Machines API access, and lower per-request latency via anycast global routing, see the Railway guide. For the Render alternative when wanting a Heroku-like PaaS with web services, background workers, cron jobs, and managed Postgres all in a single dashboard without ever touching a Dockerfile — Render is the easiest managed platform while Fly.io rewards developers who want Docker-native deployments at the edge with persistent SSHable VMs, see the Render guide. The Claude Skills 360 bundle includes Fly.io skill sets covering fly.toml, Machines API, and multi-region deployment. Start with the free tier to try global deployment generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free