Appearance
Lesson 1 — Introduction to Deployment & Cloud Hosting
In this lesson, you will take your first applications from running on your computer to being accessible on the internet.
You will learn the vocabulary of deployment, understand why software teams use different environments, and compare hosting models. Then you will publish two small projects:
- A static website on Netlify.
- A Node.js/Express API on Render.
These two examples illustrate an important idea: the way you deploy an application depends on what the application needs to run.
Your project throughout the course
Keep the Node.js API you build here. In later lessons, you will add automated tests with Vitest, package it with Docker, connect it to MySQL, and automate its deployment.
DigitalOcean will be introduced in the later cloud-deployment lesson. You do not need to set up a server or a DigitalOcean account now.
Learning outcomes
By the end of this lesson, you should be able to:
- Explain deployment, hosting, builds, environments, and releases in your own words.
- Differentiate between development, staging, and production environments.
- Choose between static hosting, a managed application platform, and a virtual private server (VPS) based on application requirements.
- Deploy a static HTML page to Netlify and a Node.js/Express API to Render.
- Verify that both applications are available publicly and identify basic deployment problems.
- Describe manual, automated, rolling, and blue-green deployment approaches, including why rollbacks matter.
What you need
You should be familiar with basic JavaScript, terminal commands, and Git. You do not need prior cloud deployment experience.
| Tool or account | Why you need it |
|---|---|
| Node.js (LTS) and npm | Run the backend locally and install its dependencies |
| Git and GitHub | Version the API and make its source available to Render |
| A code editor and a terminal | Create and edit project files |
| A Netlify account | Publish a static website |
| A Render account | Deploy an Express API as a managed web service |
A browser or curl | Test the deployed applications |
Accounts and costs
Hosting plans, included usage, and account requirements can change. Review the current options before creating services. You do not need a paid plan specifically for the examples in this lesson if a suitable free option is available to your account. Some free services have limitations; we will point out relevant ones as we go.
What you will build
text
Your computer
│
├── site/index.html ────────────────→ Netlify
│ Static website
│ Public .netlify.app URL
│
└── src/server.js + package.json ──→ GitHub ──→ Render
Express API
Public .onrender.com URLAt the end, you should have two public URLs that you can open from another device.
Part 1 — Understanding Deployment
1. What is deployment?
Deployment means making a particular version of an application available in a target environment.
For a static website, deployment might mean uploading HTML, CSS, and JavaScript files to a hosting platform. For a backend, it might mean installing dependencies, starting a Node.js process, and making it reachable over HTTPS.
A deployment usually involves more than simply copying code. Think about these questions:
- Where does the application run? On a managed platform or a server you operate?
- What does it need? A Node.js runtime, dependencies, or a database?
- How does it start? For example, with
npm start? - How is it configured? Through environment variables and platform settings?
- How do you know it works? With a browser request, an API endpoint, or health checks?
- What if something goes wrong? Can you inspect logs and return to a previous version?
A typical deployment process looks like this:
text
Write code → Test → Prepare/build → Deploy → Verify → MonitorDeployment vs. release
A deployment puts code into an environment. A release makes functionality available to users. They often happen together, but a team can deploy a feature and keep it disabled until a later release.
2. Important terminology
| Term | Meaning |
|---|---|
| Hosting | Providing the infrastructure or platform where an application runs |
| Server | A computer or service that responds to requests |
| Runtime | Software needed to execute code, such as Node.js |
| Build | A process that prepares application files for execution or publication; not every application needs one |
| Artifact | A deployable output, such as built static files or a Docker image |
| Environment | A configured place where a version of an application runs |
| Environment variable | Configuration supplied to a process without hardcoding it in source files |
| Domain / URL | An address used to access the deployed application |
| Downtime | A period when an application is unavailable |
| Rollback | Returning to a previously working version or state |
| CI/CD | Practices for automating integration, delivery, and deployment; explored in Lesson 2 |
Local is not public
If you start an API at:
text
http://localhost:3000it can usually be reached only from your own computer. The word localhost refers to the computer making the request.
After deployment, your API can receive a publicly accessible URL, for example:
text
https://your-api-name.onrender.comThe example address above is not your URL. Render will provide the actual one after you deploy.
3. Development, staging, and production
These environments allow teams to test changes before they affect real users.
| Environment | What it is for | Example |
|---|---|---|
| Development | Building features and debugging code | Running Express on your laptop |
| Staging | Checking a release in a production-like setup | Testing a candidate version before going live |
| Production | Serving real users | Your live Netlify site or Render API |
text
Development → Staging → Production
Build Validate Serve usersA small learning project may begin with only local development and a public deployment. Larger projects often introduce staging to reduce release risk.
A useful staging environment resembles production in important ways, but it should not expose production credentials or sensitive customer information.
Check your understanding
Which environment is the best fit for each activity?
- Trying a new endpoint that you expect to change repeatedly.
- Validating a version before customers see it.
- Serving requests from real customers.
- Reproducing a configuration issue before a release.
Reveal suggested answers
- Development.
- Staging.
- Production.
- Staging, ideally with relevant production-like configuration.
Part 2 — Choosing Where to Deploy
4. Common hosting models
Static hosting
Static hosting serves files such as HTML, CSS, images, and browser JavaScript. It does not require you to run a continuously listening Node.js server.
Example: a personal website, portfolio, or documentation site published on Netlify.
Platform as a Service (PaaS)
A managed application platform runs your application without requiring you to administer the underlying operating system. You typically connect a repository, set build and start commands, and configure your service.
Example: an Express API deployed as a Render Web Service.
Virtual private server (VPS)
A VPS gives you a virtual machine. You can install Node.js, Docker, MySQL, and other software yourself, but you are also responsible for maintaining the server and securing its configuration.
Example: a Linux virtual server. We will explore this approach with DigitalOcean later in the course.
5. Netlify vs. Render
For the practical work in this lesson, each platform has a specific job.
| Question | Netlify (our static site) | Render (our API) |
|---|---|---|
| What are we deploying? | HTML/CSS files | A running Node.js/Express server |
| Does our example need a Node.js server running continuously? | No | Yes, when handling requests |
| Where does the application code come from? | A local folder uploaded manually | A GitHub repository |
| How will we test it? | Open the website URL | Request / and /health |
| What will we learn? | Publishing static files | Running backend applications on a managed platform |
Netlify can also run functions, and Render can also host static sites. We are choosing these two paths because they make the difference between static hosting and backend hosting easy to see.
Choose the hosting model
Which option is a good starting point for each project?
| Project | Consider the requirements |
|---|---|
| A landing page made of HTML and CSS | No server-side logic |
| A Node.js REST API | Needs a runtime and a listening process |
| An application whose team must install and configure its own services on Linux | Needs direct server control |
Reveal possible answers
- Landing page: static hosting such as Netlify.
- Node.js REST API: a managed web service such as Render.
- Full Linux control: a VPS, such as a DigitalOcean Droplet (covered later).
These are examples, not exclusive rules. The right answer depends on the application and operational requirements.
Part 3 — Practical Example: Deploy a Static Site to Netlify
In this first deployment, you will publish a single HTML page. No backend or build step is required.
6. Create the project
Open a terminal and create a project directory:
bash
mkdir deployment-workshop
cd deployment-workshop
mkdir siteCreate site/index.html with this content:
html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>My First Deployment</title>
<style>
body {
font-family: system-ui, sans-serif;
max-width: 640px;
margin: 4rem auto;
padding: 0 1rem;
line-height: 1.6;
}
h1 { color: #186b57; }
</style>
</head>
<body>
<h1>Hello from Netlify!</h1>
<p>This is my first publicly deployed website.</p>
<p>I deployed it by publishing a folder of static files.</p>
</body>
</html>Open site/index.html in your browser to check it locally.
Why is there no npm start?
This page is just HTML and CSS. Your browser can display it directly, and Netlify can serve the same files without running an Express application.
7. Publish the site on Netlify
- Sign in to Netlify.
- Open Netlify Drop, or choose the dashboard option to add a project by dragging and dropping files.
- Upload the
sitefolder, not the entiredeployment-workshopfolder. The uploaded folder must haveindex.htmlat its top level. - Open the site URL that Netlify provides.
- Verify that the heading “Hello from Netlify!” is visible.
Your page should now be available at an address similar to:
text
https://your-site-name.netlify.appMake a change and redeploy
Update the text in site/index.html, save the file, and upload the updated site folder to your existing Netlify project using its deploy interface.
Refresh the public page to confirm that your change is visible.
What did you just do?
You performed a manual deployment: you explicitly selected files and published them. Later, you will see how connecting a repository lets a hosting platform deploy changes automatically.
Common Netlify problems
| Problem | What to check |
|---|---|
| Page not found | Did you upload the folder containing index.html, rather than its parent? |
| The old page is still shown | Did you deploy the updated folder to the same project? Try refreshing the page. |
| HTML file downloads or renders unexpectedly | Check that the file is actually named index.html and contains valid HTML. |
Your Netlify checkpoint
- [ ] My website works locally.
- [ ] I can open it using my Netlify URL.
- [ ] I published an updated version successfully.
- [ ] I can explain why this page does not need a continuously running backend.
Part 4 — Practical Example: Deploy a Node.js API to Render
Next, you will deploy a backend. Unlike the static page, this application needs Node.js to run server-side code when requests arrive.
You will keep this API for the following lessons.
8. Set up an Express application
Return to your existing project directory:
bash
cd deployment-workshop
npm init -y
npm install express
npm pkg set type=module
npm pkg set scripts.start="node src/server.js"
npm pkg set scripts.dev="node --watch src/server.js"
mkdir srcAlready inside the project?
If your terminal is already in deployment-workshop, do not run the cd deployment-workshop line again.
Create src/server.js:
js
import express from 'express';
const app = express();
const port = Number(process.env.PORT || 3000);
app.use(express.json());
app.get('/', (_req, res) => {
res.json({ message: 'Hello from Render!' });
});
app.get('/health', (_req, res) => {
res.status(200).json({ status: 'ok' });
});
app.listen(port, '0.0.0.0', () => {
console.log(`API listening on port ${port}`);
});Three details matter here:
process.env.PORTallows the hosting environment to configure the port. You should not hardcode a production port.0.0.0.0lets the application listen on the container or server network interface rather than only the loopback interface./healthgives you a basic endpoint for checking whether the API process is responding.
9. Test the API locally
Start the application:
bash
npm startVisit these URLs in a browser, or run the following commands in another terminal:
bash
curl http://localhost:3000/
curl http://localhost:3000/healthExpected responses:
json
{"message":"Hello from Render!"}json
{"status":"ok"}Stop the local server with Ctrl+C after testing.
10. Prepare your Git repository
Create .gitignore in the project root:
text
node_modules/
.env
.env.*
!.env.example
coverage/
.DS_StoreCreate .env.example to document local configuration:
dotenv
PORT=3000
NODE_ENV=developmentNever commit secrets
Never commit actual .env files, passwords, API keys, or database credentials. The .env.example file only documents possible settings; our current server does not load it automatically. You will learn more about environment configuration in Lesson 4.
Your folder structure should now look like this:
text
deployment-workshop/
├── site/
│ └── index.html
├── src/
│ └── server.js
├── .env.example
├── .gitignore
├── package.json
└── package-lock.jsonIf this is a new Git project, initialize it and commit your work:
bash
git init
git add .
git commit -m "Add static site and Express API"
git branch -M mainCreate an empty GitHub repository called deployment-workshop (do not initialize it with a README). Connect it using your own repository URL:
bash
git remote add origin https://github.com/YOUR_USERNAME/deployment-workshop.git
git push -u origin mainReplace YOUR_USERNAME with your GitHub username. GitHub may ask you to authenticate; follow its instructions. If your repository already has a remote, use that remote instead of adding another.
11. Deploy the API on Render
- Sign in to Render.
- Select New → Web Service.
- Connect your GitHub account and select the
deployment-workshoprepository. - Configure the service with the values below.
- Review the available instance plans and choose one suitable for learning.
- Create the service and inspect its deployment output.
| Setting | Value |
|---|---|
| Service type | Web Service |
| Language / runtime | Node |
| Repository | Your deployment-workshop repository |
| Branch | main |
| Root directory | Repository root (. or the default) |
| Build command | npm ci |
| Start command | npm start |
You do not need to set a custom PORT value for this example; Render supplies the appropriate port setting. The npm ci command uses the committed package-lock.json to install dependencies reproducibly.
When deployment succeeds, Render provides a URL similar to:
text
https://your-api-name.onrender.comFree instance behavior
If you select Render's Free web service plan, the service may spin down after a period of inactivity. The next request can take longer while it starts again. That delay does not automatically mean your code is broken. Check Render's current plan documentation for details.
12. Verify the public API
Replace YOUR_RENDER_URL below with the public URL shown in your Render dashboard:
bash
curl https://YOUR_RENDER_URL/healthExpected response:
json
{"status":"ok"}Also open the root URL to check the greeting:
json
{"message":"Hello from Render!"}Try opening the same /health URL on another device. This demonstrates the difference between localhost and a public deployment.
Update the API and observe deployment behavior
Change the / response message in src/server.js. Commit and push:
bash
git add src/server.js
git commit -m "Update API greeting"
git pushReturn to Render and inspect your service's deployment activity. A Git-connected Render service normally starts a new deployment after changes are pushed to the linked branch, when automatic deployment is enabled.
Once the deployment succeeds, request the root URL again to see the new message.
Compare the two workflows:
- With Netlify Drop, you published files manually.
- With your Git-connected Render service, you pushed code and the platform performed the deployment steps.
Git-connected deployment is useful, but it is not the same as a fully tested CI/CD pipeline. In Lesson 2, you will add automated tests using Vitest and GitHub Actions.
Common Render problems
| Problem | What to check |
|---|---|
| Build failed | Confirm that package.json and package-lock.json are committed, and inspect the build logs. |
| Service did not start | Confirm the start command is npm start and the script runs locally. |
| Service cannot be reached | Check the deploy status, the public URL, and the app's listening port. |
/health returns 404 | Confirm the route exists and that you deployed the latest commit. |
| First request takes a while | A free service might be waking up after inactivity. |
Your Render checkpoint
- [ ]
npm startworks locally. - [ ] My API source code is in GitHub.
- [ ] Render reports a successful deployment.
- [ ] The public
/healthendpoint returns{"status":"ok"}. - [ ] I pushed a change and observed a new deployment.
Part 5 — Deployment Strategies and Recovery
13. Manual vs. automated deployment
You have now seen two ways to publish code:
- Manual deployment: you selected and uploaded the static files to Netlify.
- Platform-triggered deployment: you pushed a Git commit and Render deployed the linked branch.
A more complete automated pipeline might look like this:
text
Push code → Run tests → Build → Deploy → VerifyWe will construct this sort of workflow during the course. Automation makes a process more repeatable, but it does not replace testing, security reviews, or monitoring.
14. How can new versions replace old versions?
Recreate (stop and replace)
text
Version A → stop → start Version BSimple to understand, but the application may be briefly unavailable.
Rolling deployment
text
[A] [A] [A]
[B] [A] [A]
[B] [B] [A]
[B] [B] [B]Instances are replaced gradually. This can reduce downtime, but requires enough capacity and compatibility between versions.
Blue-green deployment
text
Before: users → Blue (A) Green (B) ready
After: users → Green (B) Blue (A) retained temporarilyTwo environments are prepared, and traffic is switched to the new one after validation. This can make reverting application traffic easier, but adds infrastructure and configuration complexity.
Rollback
A rollback returns an application to a previously working version or state. Before deploying a change, ask yourself:
- Which version worked before this change?
- Can I deploy it again?
- How will I confirm that recovery succeeded?
- Could a database change make returning to the old version unsafe?
Rollbacks and databases
Reverting application code does not automatically restore database schemas or data. You will explore MySQL migrations, backups, and restoration in later lessons.
Think about this
You changed the Express API response and deployed the new version on Render, but the public endpoint now returns an error. What would you check first? What would you do if you could not fix it quickly?
Suggested approach
First inspect Render's deployment status and logs, verify the configured start command, and check whether the expected code was deployed. If the problem affects users and cannot be resolved safely, use an available rollback or redeploy a known working commit, then verify /health and the API response.
Final Exercise — Explain Your Deployments
Complete the table using the actual URLs from your accounts.
| Question | Your answer |
|---|---|
| What is your Netlify website URL? | Add your URL |
| What files did Netlify publish? | Your answer |
| What is your Render API URL? | Add your URL |
| What command starts your Express API? | Your answer |
| Which of your deployments required a running Node.js server? | Your answer |
| How did you publish an update to Netlify? | Your answer |
| How did you publish an update to Render? | Your answer |
Optional challenge — Link the two sites
Add a link to your Netlify page that opens your Render API's /health endpoint:
html
<a href="https://YOUR_RENDER_URL/health" target="_blank" rel="noopener noreferrer">
View my API health check
</a>Replace the example URL with your own, then manually redeploy the updated site folder to Netlify.
This is only a link to the API, so no cross-origin browser API request is needed. You will learn how to build more complex integrations later.
Knowledge Check
Try answering these questions before revealing the answers.
- What is the difference between deployment and release?
- Why is
localhostnot a suitable public API URL? - What is the purpose of staging?
- Why is static hosting a good fit for the HTML page?
- Why does our Express API need a backend hosting service?
- What do Render's build and start commands do?
- What was manual about the Netlify deployment?
- What event triggered a new Render deployment?
- How does a rolling deployment differ from a blue-green deployment?
- What should you verify after a deployment or rollback?
Reveal suggested answers
- Deployment installs or publishes a version; release makes functionality available to users.
localhostpoints to the machine making the request, not your publicly accessible host.- Staging lets you verify changes in a production-like environment before users are affected.
- The browser only needs static files; no continuously running backend is required.
- Express handles HTTP requests in a running Node.js process.
- The build command prepares dependencies or build output; the start command launches the application.
- You explicitly uploaded the folder and then repeated the upload to publish a change.
- A push to the linked Git branch, when automatic deployment was enabled.
- Rolling deployment replaces instances incrementally; blue-green switches traffic between separately prepared environments.
- Confirm the service is available and that important routes and behaviors work as expected.
Summary
You deployed two different types of application:
- A static website to Netlify, by publishing a folder of HTML files.
- A Node.js/Express API to Render, by connecting a GitHub repository and configuring a web service.
You also learned that development, staging, and production environments serve different purposes, and that deployment strategies affect how safely new versions reach users.
The key takeaway is this: deploying a static site is different from deploying a backend because their runtime requirements are different.
Before Lesson 2
- [ ] Keep the
deployment-workshopproject on your computer. - [ ] Make sure the Express API is committed and pushed to GitHub.
- [ ] Check that both your Netlify website and Render API URLs still work.
- [ ] Be ready to explain the difference between a manual deployment and a Git-triggered deployment.
Next: Lesson 2 — Continuous Integration & Continuous Deployment (CI/CD). You will add Vitest tests and create a GitHub Actions workflow for your API.