# Deploy Your First App
You've got a live app running on Zerops. Now let's make it actually yours.
This page walks you through deploying the feedback app we showed you at the top of the quickstart - a Node.js app with a PostgreSQL database, auto-deploy on every git push, and a wall of everyone who's made it through. If you'd rather skip straight to your own code, there's a note at the bottom for that.
### Deploy the feedback app
Start from the recipe template. Click the link below, pick a name for your repo, and hit **Create repository**:
**[github.com/new?template_name=recipe-nodejs&template_owner=zeropsio](https://github.com/new?template_name=recipe-nodejs&template_owner=zeropsio)**
Then clone your new repo and install dependencies:
```bash
git clone https://github.com/your-username/your-repo.git
cd your-repo
npm install
```
The repo uses TypeScript. You only need to touch two files: `src/app.ts` and `src/db.ts`. Leave `src/index.ts` and `src/config.ts` exactly as they are.
**Replace `src/db.ts`** with this:
```ts
export const connectDB = async () => {
const client = new Client({
host: config.db.host,
port: config.db.port,
user: config.db.username,
password: config.db.password,
database: config.db.database,
});
await client.connect();
await client.query(`
CREATE TABLE IF NOT EXISTS clicks (
id SERIAL PRIMARY KEY,
seed INTEGER NOT NULL,
clicked_at TIMESTAMPTZ DEFAULT NOW()
)
`);
return client;
};
```
**Replace `src/app.ts`** with this:
```ts
const app = express();
app.use(express.static(path.join(__dirname, '../public')));
app.get('/count', async (_, res) => {
const client = await connectDB();
const result = await client.query(
'SELECT seed FROM clicks ORDER BY id ASC LIMIT 20'
);
const countResult = await client.query('SELECT COUNT(*) FROM clicks');
await client.end();
res.json({
count: parseInt(countResult.rows[0].count),
seeds: result.rows.map((r) => r.seed),
});
});
app.post('/click', async (_, res) => {
const client = await connectDB();
const seed = Math.floor(Math.random() * 1000000);
await client.query('INSERT INTO clicks (seed) VALUES ($1)', [seed]);
const countResult = await client.query('SELECT COUNT(*) FROM clicks');
await client.end();
res.json({ count: parseInt(countResult.rows[0].count), seed });
});
app.get('/status', (_, res) => {
res.status(200).send({ status: 'UP' });
});
export default app;
```
**Create a `public/` folder** at the repo root and add `public/index.html`:
```html
Zerops Quickstart
zerops
You made it. 🎉
You just deployed a real app: managed database, private network,
auto-deploy. Let us know you made it through.
...
Node.js 20PostgreSQL 16Zerops
```
The `zerops.yml` already exists in the repo. Update `deployFiles` to include the `public` folder:
```yaml
zerops:
- setup: app
build:
base: nodejs@20
prepareCommands:
- npm install -g typescript
buildCommands:
- npm i
- npm run build
deployFiles:
- ./dist
- ./node_modules
- ./public
- ./package.json
run:
base: nodejs@20
ports:
- port: 3000
httpSupport: true
envVariables:
NODE_ENV: production
DB_NAME: db
DB_HOST:
DB_USER:
DB_PASSWORD:
# or use the full connection string:
# DB_CONNECTION_STRING:
start: npm run start:prod
healthCheck:
httpGet:
port: 3000
path: /status
```
:::tip How Zerops env variables work
Zerops automatically generates credentials for every managed service. The variable names are derived from the service hostname — so if your database service is named `db`, the variables are ``, ``, ``, and ``. If you named it `postgres` instead, they'd be ``, ``, and so on.
:::
Push to your repo and connect GitHub in the next section.
```bash
git add .
git commit -m "add feedback app"
git push
```
:::note Want to build something else instead?
Skip the feedback app. Pick the recipe matching your stack from [app.zerops.io/recipes](https://app.zerops.io/recipes), add a `zerops.yaml` to your repo root copying the structure from the recipe, and adjust `buildCommands`, `deployFiles`, and `start` for your stack. The database env variables (``, ``, ``) stay the same regardless of what you're building.
:::
### Connect GitHub and auto-deploy
1. Click into your **app** service
2. Scroll down to **Pipelines & CI/CD settings**
3. Click **GitHub** to connect your repo
4. Select your repo and set **Trigger on** to **Push to Branch**, pick `main`
5. In the **"Which `setup` from zerops.yml to use"** field, type `app`
6. Click **Activate pipeline trigger**
That's it. Every push to main now builds and deploys automatically. Zero downtime, Zerops runs the new version alongside the old one, waits for a health check, then switches traffic over.
You can also trigger deploys manually with the Zerops CLI: `zcli push`.
### Add yourself to the list
Deployed the feedback app? Open your live app URL and click **"I followed the Zerops quickstart"**. You'll show up alongside everyone else who's made it through.
Check out everyone who's already made it: [app-25be-3000.prg1.zerops.app](https://app-25be-3000.prg1.zerops.app/)
### If something breaks
Got a 502 or an app crash on startup? Start here.
**Check the runtime logs first.** Dashboard, click your app service, click the three-dot menu, then **Runtime log**. The error will be there, usually in the last few lines.
Two things come up most often on a first deploy:
:::tip Debug locally with VPN
Install zcli first (see [CLI reference](/references/cli)), then run `zcli vpn up [your-project-id]` and your machine joins the project's private network. You can connect to `db:5432` directly from your local machine using TablePlus, psql, or any database client. You can disable SSL when connecting over VPN - the tunnel itself handles security either way. If `db` doesn't resolve, try `db.zerops` instead.
:::
### What's next
- **[SSH into your container](/references/networking/ssh)**: `zcli service shell [service-name]` for full Linux access
- **[Custom domain](/references/networking/public-access)**: add your domain, SSL is automatic
- **[Autoscaling](/features/scaling)**: set min and max CPU and RAM, Zerops scales within that range automatically
- **[Add more services](/features/infrastructure)**: queues, search engines, object storage, just add them to your project
- **[Try ZCP](/zcp/quickstart)**: Zerops' AI agent that can deploy, debug, and operate your project
:::note Stuck?
Jump into the [Zerops Discord](https://docs.zerops.io/discord). The community is active and the team is there.
:::