{"id":124774,"date":"2026-07-31T13:39:03","date_gmt":"2026-07-31T08:09:03","guid":{"rendered":"https:\/\/www.guvi.in\/blog\/?p=124774"},"modified":"2026-07-31T13:39:06","modified_gmt":"2026-07-31T08:09:06","slug":"fly-io-tutorial","status":"publish","type":"post","link":"https:\/\/www.guvi.in\/blog\/fly-io-tutorial\/","title":{"rendered":"Fly.io Tutorial: Step-by-Step Guide to Deploy Your App"},"content":{"rendered":"\n<p>Deploying an application can feel complex when you are moving from local development to production. Fly.io makes this process easier by helping developers deploy apps globally, manage secrets, configure domains, monitor logs, and scale services through a simple CLI-based workflow.<\/p>\n\n\n\n<p>In this Fly.io tutorial, we will walk through the complete deployment process, from installing flyctl and creating a Fly.io app to setting environment variables, connecting databases, checking logs, and launching a production-ready application.<\/p>\n\n\n\n<p>Read the full blog to learn how to deploy your app on Fly.io step by step.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>TL;DR<\/strong><\/h2>\n\n\n\n<ul>\n<li>Fly.io helps developers deploy apps, APIs, and backend services across global regions.<\/li>\n\n\n\n<li>The deployment process starts with installing flyctl, logging in, and running fly launch.<\/li>\n\n\n\n<li>Your app must use the correct internal port, usually 8080, for smooth routing.<\/li>\n\n\n\n<li>Secrets, databases, logs, custom domains, and scaling can be managed through Fly.io commands.<\/li>\n\n\n\n<li>A successful Fly.io deployment needs correct fly.toml settings, clean logs, and proper production testing.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Fly.io Deployment Tutorial: Step-by-Step Guide to Deploy Your App<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 1: Install flyctl<\/strong><\/h3>\n\n\n\n<p>Install flyctl before deploying your application. It is the command-line tool used to create Fly.io apps, authenticate accounts, deploy code, manage Machines, configure secrets, and check logs.<\/p>\n\n\n\n<p>For macOS, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>brew install flyctl<\/code><\/pre>\n\n\n\n<p>For <a href=\"https:\/\/www.guvi.in\/blog\/read-logs-and-work-with-linux\/\" target=\"_blank\" rel=\"noreferrer noopener\">Linux<\/a> or macOS without Homebrew, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -L https:\/\/fly.io\/install.sh | sh<\/code><\/pre>\n\n\n\n<p>For Windows, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pwsh -Command \"iwr https:\/\/fly.io\/install.ps1 -useb | iex\"<\/code><\/pre>\n\n\n\n<p>Fly.io recommends installing the correct flyctl version based on your operating system.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 2: Log in to Fly.io<\/strong><\/h3>\n\n\n\n<p>After installing flyctl, log in to your Fly.io account.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly auth login<\/code><\/pre>\n\n\n\n<p>New users can create an account directly through the CLI.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly auth signup<\/code><\/pre>\n\n\n\n<p>This connects your local system with Fly.io and allows deployments from the terminal. Fly.io\u2019s quickstart flow uses fly auth signup or fly auth login before launching an app.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 3: Prepare Your Application for Deployment<\/strong><\/h3>\n\n\n\n<p>Before deployment, test your app locally and confirm that all dependencies install correctly. Your application should have a valid production start command.<\/p>\n\n\n\n<p>For a <a href=\"https:\/\/www.guvi.in\/blog\/best-nodejs-frameworks-guide\/\" target=\"_blank\" rel=\"noreferrer noopener\">Node.js app<\/a>, package.json may include:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n\n&nbsp;&nbsp;\"scripts\": {\n\n&nbsp;&nbsp;&nbsp;&nbsp;\"start\": \"node server.js\"\n\n&nbsp;&nbsp;}\n\n}<\/code><\/pre>\n\n\n\n<p>Your app should also include all required files, such as package.json, lock files, framework configuration, and production-ready environment handling.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 4: Configure the Application Port<\/strong><\/h3>\n\n\n\n<p>Fly.io routes traffic to the internal port defined in fly.toml. The default internal_port is 8080, and Fly.io recommends using this default for most applications.&nbsp;<\/p>\n\n\n\n<p>For a Node.js Express app, configure the server like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const express = require(\"express\");\n\nconst app = express();\n\nconst PORT = process.env.PORT || 8080;\n\napp.get(\"\/\", (req, res) =&gt; {\n\n&nbsp;&nbsp;res.send(\"App running on Fly.io\");\n\n});\n\napp.listen(PORT, \"0.0.0.0\", () =&gt; {\n\n&nbsp;&nbsp;console.log(`Server running on port ${PORT}`);\n\n});<\/code><\/pre>\n\n\n\n<p>The app must listen on the same port defined in fly.toml. It should also bind to 0.0.0.0, so Fly.io can route external traffic into the running Machine.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 5: Add a Dockerfile When Needed<\/strong><\/h3>\n\n\n\n<p>Fly.io deploys applications as <a href=\"https:\/\/www.guvi.in\/blog\/containerization-using-docker\/\" target=\"_blank\" rel=\"noreferrer noopener\">Docker<\/a> images. Fly Launch can detect a Dockerfile, build it, and deploy the app automatically.<\/p>\n\n\n\n<p>A simple Node.js Dockerfile may look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FROM node:20-alpine\n\nWORKDIR \/app\n\nCOPY package*.json .\/\n\nRUN npm ci --omit=dev\n\nCOPY . .\n\nEXPOSE 8080\n\nCMD &#91;\"npm\", \"start\"]<\/code><\/pre>\n\n\n\n<p>The EXPOSE 8080 instruction helps Fly.io detect the service port during launch. When no port is detected, Fly.io uses 8080as the default internal port.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 6: Create a New Fly.io App<\/strong><\/h3>\n\n\n\n<p>Move into your project folder and run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly launch<\/code><\/pre>\n\n\n\n<p>Fly Launch scans your source code, detects common frameworks, creates a Fly.io app, generates a fly.toml file, and deploys the app for most supported projects.&nbsp;<\/p>\n\n\n\n<p>Use this option when you want to review configuration before deployment:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly launch --no-deploy<\/code><\/pre>\n\n\n\n<p>This is useful when you need to edit secrets, ports, regions, or service settings before the first deployment.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 7: Review the fly.toml File<\/strong><\/h3>\n\n\n\n<p>Fly.io stores app deployment settings inside fly.toml. This file can define the app name, primary region, build settings, environment variables, public services, ports, volumes, and release commands.&nbsp;<\/p>\n\n\n\n<p>A basic fly.toml file may look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app = \"your-app-name\"\n\nprimary_region = \"sin\"\n\n&#91;build]\n\n&#91;http_service]\n\n&nbsp;&nbsp;internal_port = 8080\n\n&nbsp;&nbsp;force_https = true\n\n&nbsp;&nbsp;auto_stop_machines = \"stop\"\n\n&nbsp;&nbsp;auto_start_machines = true\n\n&nbsp;&nbsp;min_machines_running = 0<\/code><\/pre>\n\n\n\n<p>The primary_region tells Fly.io where to create new Machines. The internal_port tells Fly Proxy which port your application uses inside the Machine.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 8: Set Environment Variables and Secrets<\/strong><\/h3>\n\n\n\n<p>Production apps often need API keys, database URLs, JWT secrets, payment keys, and service tokens. Do not hardcode these values in source code.<\/p>\n\n\n\n<p>Set encrypted secrets using:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly secrets set NODE_ENV=production JWT_SECRET=your_secret_key\n\nFly.io uses fly secrets set to store one or more encrypted secrets for an application.&nbsp;\n\nFor non-sensitive values, you can use the &#91;env] section inside fly.toml.\n\n&#91;env]\n\n&nbsp;&nbsp;NODE_ENV = \"production\"<\/code><\/pre>\n\n\n\n<p>Secrets should be used for private values. The [env] section should be used only for safe configuration values.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 9: Add a PostgreSQL Database<\/strong><\/h3>\n\n\n\n<p>Fly.io offers Managed Postgres for production database needs. You can create a Managed Postgres cluster and attach it to your application through the dashboard or CLI.&nbsp;<\/p>\n\n\n\n<p>Create a Managed Postgres cluster using:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly mpg create\n\nThen attach the database to your app:\n\nfly mpg attach &lt;clusterID&gt; -a &lt;app-name&gt;<\/code><\/pre>\n\n\n\n<p>Fly.io adds the database connection details as a secret, usually named DATABASE_URL. Your app can then read the connection string from process.env.DATABASE_URL.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 10: Deploy the Application<\/strong><\/h3>\n\n\n\n<p>Run the deployment command from your project directory:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly deplo<\/code><\/pre>\n\n\n\n<p>The fly deploy command builds your app, creates a Docker image, pushes it to Fly.io\u2019s internal registry, and starts it on one or more Fly Machines using the local fly.toml configuration.&nbsp;<\/p>\n\n\n\n<p>Run this command whenever you update your code or change deployment configuration.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 11: Check the Deployment Status<\/strong><\/h3>\n\n\n\n<p>After deployment, check the current app status.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly status<\/code><\/pre>\n\n\n\n<p>This command shows application details, recent deployment details, Machine allocation, and active regions.&nbsp;<\/p>\n\n\n\n<p>Open the deployed app in your browser:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly apps open<\/code><\/pre>\n\n\n\n<p>The command opens the current deployed application from the terminal.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 12: View Application Logs<\/strong><\/h3>\n\n\n\n<p>Use logs to debug failed builds, missing secrets, startup errors, runtime crashes, and <a href=\"https:\/\/www.guvi.in\/blog\/database-management-guide-with-examples\/\" target=\"_blank\" rel=\"noreferrer noopener\">database connection problems.<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly logs<\/code><\/pre>\n\n\n\n<p>By default, fly logs streams application logs continuously until the command is stopped. You can also fetch buffered logs without tailing.&nbsp;<\/p>\n\n\n\n<p>Use this command when the app deploys successfully but does not respond correctly.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly logs --no-tail<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 13: Connect a Custom Domain<\/strong><\/h3>\n\n\n\n<p>Fly.io gives every app a .fly.dev subdomain for testing. For production, attach your custom domain and configure <a href=\"https:\/\/www.guvi.in\/blog\/what-is-a-domain-name-system\/\" target=\"_blank\" rel=\"noreferrer noopener\">DNS records<\/a> through your domain provider.\u00a0<\/p>\n\n\n\n<p>Add a custom domain using:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly certs add example.com<\/code><\/pre>\n\n\n\n<p>Check certificate and DNS status using:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly certs check example.com<\/code><\/pre>\n\n\n\n<p>Fly.io shows the required DNS configuration after adding the domain. A and AAAA records are recommended for direct app connections, while CNAME records work well for subdomains.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 14: Configure HTTPS and Certificate Validation<\/strong><\/h3>\n\n\n\n<p>Fly.io uses Let\u2019s Encrypt certificates for custom domains after domain ownership is verified. Certificate validation may use TLS-ALPN, HTTP-01, or DNS-01 validation, depending on your domain setup.&nbsp;<\/p>\n\n\n\n<p>Use this command to view setup instructions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly certs setup example.com<\/code><\/pre>\n\n\n\n<p>DNS changes may take time to propagate. Certificate validation can fail when records are missing, incorrect, proxied incorrectly, or rate-limited.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 15: Scale the Application<\/strong><\/h3>\n\n\n\n<p>Fly.io allows apps to scale by Machine count, VM size, memory, and region. To increase Machine count, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly scale count <\/code><\/pre>\n\n\n\n<p>The fly scale count command changes the app\u2019s VM count to the given value.&nbsp;<\/p>\n\n\n\n<p>Check the current scale configuration using:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly scale show<\/code><\/pre>\n\n\n\n<p>Use scaling carefully because more Machines and larger VM sizes can increase hosting cost.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 16: Test the Production Deployment<\/strong><\/h3>\n\n\n\n<p>After deployment, test the full production flow. Check pages, API routes, authentication, database reads, database writes, webhooks, file uploads, and third-party integrations.<\/p>\n\n\n\n<p>Test common endpoints such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GET \/\n\nGET \/health\n\nPOST \/api\/login\n\nGET \/api\/users<\/code><\/pre>\n\n\n\n<p>Also confirm that frontend URLs, backend URLs, CORS settings, cookies, and callback URLs use the production Fly.io domain or custom domain.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 17: <\/strong><a href=\"https:\/\/www.guvi.in\/blog\/debugging-in-software-development\/\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Debug<\/strong><\/a><strong> Common Fly.io Deployment Errors<\/strong><\/h3>\n\n\n\n<p>Common Fly.io deployment issues usually come from wrong ports, missing secrets, failed Docker builds, database connection errors, or incorrect start commands.<\/p>\n\n\n\n<p>Check these points first:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fly logs\n\nfly status\n\nfly secrets list\n\nfly config show<\/code><\/pre>\n\n\n\n<p>Confirm that your app listens on the same port as internal_port. Also confirm that private values are stored with fly secrets set and not inside committed source code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 18: Redeploy After Code Changes<\/strong><\/h3>\n\n\n\n<p>After updating your application, commit the changes and redeploy.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git add .\n\ngit commit -m \"Update Fly.io deployment\"\n\nfly deploy<\/code><\/pre>\n\n\n\n<p>Fly.io builds a new image and updates or creates Machines during each deployment. This keeps your production app aligned with the latest source code and configuration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h2>\n\n\n\n<p>Fly.io gives developers a practical way to deploy full-stack apps, APIs, backend services, Dockerized projects, and database-connected applications. A successful Fly.io deployment depends on a working local app, correct port configuration, valid fly.toml settings, secure secrets, clean logs, and proper domain setup.<\/p>\n\n\n\n<p>The most important technical detail is port handling. Your app should listen on the same internal port configured in fly.toml, usually 8080. After deployment, use fly status, fly logs, fly apps open, and fly certs check to verify that the app works correctly in production.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>FAQs<\/strong><\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list \">\n<div id=\"faq-question-1784585027653\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>What is Fly.io?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Fly.io is a cloud platform that helps developers deploy applications, APIs, and backend services close to users across global regions.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1784585174747\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>What is flyctl?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>flyctl is the official Fly.io command-line tool used to create apps, deploy code, manage secrets, check logs, and scale services.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1784585184397\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>What is fly.toml in Fly.io?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>fly.toml is the Fly.io configuration file that stores app settings such as app name, region, internal port, services, and deployment rules.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1784585209364\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>What is the default port for Fly.io apps?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Fly.io commonly uses 8080 as the internal application port. Your app should listen on the same port defined in fly.toml.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1784585226632\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>What is Fly.io used for?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Fly.io is used to deploy web apps, APIs, backend services, Dockerized applications, and database-connected projects in production.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Deploying an application can feel complex when you are moving from local development to production. Fly.io makes this process easier by helping developers deploy apps globally, manage secrets, configure domains, monitor logs, and scale services through a simple CLI-based workflow. In this Fly.io tutorial, we will walk through the complete deployment process, from installing flyctl [&hellip;]<\/p>\n","protected":false},"author":60,"featured_media":128503,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[294],"tags":[],"views":"27","authorinfo":{"name":"Vaishali","url":"https:\/\/www.guvi.in\/blog\/author\/vaishali\/"},"thumbnailURL":"https:\/\/www.guvi.in\/blog\/wp-content\/uploads\/2026\/07\/Fly.io-Tutorial-300x116.webp","_links":{"self":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/124774"}],"collection":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/users\/60"}],"replies":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/comments?post=124774"}],"version-history":[{"count":3,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/124774\/revisions"}],"predecessor-version":[{"id":128509,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/124774\/revisions\/128509"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media\/128503"}],"wp:attachment":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media?parent=124774"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/categories?post=124774"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/tags?post=124774"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}