# Deployment

## Deployment & Launch

Taskly builds a static site into `dist/`, so it runs on any static host. It ships ready-made settings for **Netlify**, **Vercel**, and **Cloudflare Pages**.

| Setting           | Value                   |
| ----------------- | ----------------------- |
| Build command     | `npm run build`         |
| Output directory  | `dist`                  |
| Node.js version   | 22.12.0 or later        |
| Package manager   | npm (`package-lock.json`) |

### Before You Launch
Go through this list once. Each item links to the page that explains it.

- [ ] `[site] baseUrl` is your real address, not the shipped `https://example.com` placeholder, and `npm run build` shows no `[site]` warning ([SEO & AI Visibility](/taskly/customization/seo-ai-visibility/))
- [ ] Site name, tagline (`config.toml` and `site.tagline` in the i18n files), logo (and `logoDark` for the dark footer if your logo is dark), favicon, social image, and colors are yours ([Quick Start](/taskly/getting-started/quick-start/))
- [ ] The homepage `metaTitle` and `metaDescription` are yours (they name the theme as shipped)
- [ ] The contact, demo request, and newsletter forms post to your own accounts ([Forms](/taskly/customization/forms/))
- [ ] The emails, booking slots, and support details on the contact page are yours (`contact-cards-section.md` and `contact-message-section.md`)
- [ ] The log-in and sign-up screens post to your app (`form.action` in `src/content/auth/`), or are deleted
- [ ] `PUBLIC_DEMO_MODE` is not set in your host's environment variables or a `.env` file
- [ ] The **Home** and **Pages** demo menus and the **Homepages** footer column are hidden or replaced ([Menus](/taskly/customization/menus/))
- [ ] Your homepage uses the footer you want (`footerLayout`: `wordmark` on `/` as shipped) and the `wordmark` footer shows your name ([Footer & CTA](/taskly/customization/footer/))
- [ ] Demo pages you don't need are deleted, for example the extra homepages, `/elements/`, and `/status/` if you don't run a status page ([Pages](/taskly/customization/pages/))
- [ ] Sample content is replaced: reviews and ratings, customer logos, people, prices, numbers, the dashboard sample data, blog posts, changelog, integrations, templates, solutions, and help links (`#` placeholders). The sample job roles are deleted or replaced with real openings ([Collections](/taskly/customization/collections/))
- [ ] The status page data is yours, or the page is a draft
- [ ] The announcement bar has your message, or is turned off
- [ ] The privacy policy, terms, and cookie policy are reviewed for your business, and their sample `notice` removed
- [ ] Social links in `src/config/social.json` are yours
- [ ] Analytics scripts are in `[head]`, gated on consent if the cookie banner is on ([Cookies & Analytics](/taskly/customization/cookies-analytics/))
- [ ] `npm run astro-check` and `npm run build` finish without errors, and you have opened every page you changed

### Deploy
<Tabs>
  <TabItem label="Netlify">

    <Steps>

    1. Push your project to GitHub, GitLab, or Bitbucket.
    2. In Netlify, choose **Add new site → Import an existing project** and pick the repository.
    3. Netlify reads `netlify.toml`: build command `npm run build`, publish folder `dist`, Node.js 22. Click **Deploy**.

    </Steps>

    The build uses `baseUrl` from `config.toml` unless you add a `SITE_URL` environment variable in the Netlify site settings.

    `netlify.toml` also sets long-term caching for `/_astro/*` and `/images/*`, and sends missing `/fr/...` URLs to the French 404 page. Add a block for each extra language.

    Using Netlify Forms? Set `contactFormProvider = "netlify"` and `contactFormAction = "/"`, enable form detection under **Forms**, and redeploy.

  </TabItem>
  <TabItem label="Vercel">

    <Steps>

    1. Push your project to GitHub, GitLab, or Bitbucket.
    2. In Vercel, choose **Add New → Project** and import the repository. Framework preset: **Astro**.
    3. Vercel reads `vercel.json`, which builds with `sh vercel.sh`. Click **Deploy**.

    </Steps>

    `vercel.sh` sets the site address (`SITE_URL`) for each build, and on Vercel it replaces `baseUrl`:

    - Production builds use the `SITE_URL` environment variable if you set one in the Vercel project settings. Otherwise they use your Vercel production domain. Set `SITE_URL` (for example `https://www.acme.dev`) when you want one exact address, such as the `www` or the bare domain.
    - Preview builds use `https://staging.<your production domain>`.

    Edit `vercel.sh` if you want something else.

    `vercel.json` also enables trailing slashes, redirects `/sitemap.xml` to `/sitemap-index.xml`, and serves the `.md` and `llms` files as text.

  </TabItem>
  <TabItem label="Cloudflare Pages">

    **From git:** in the Cloudflare dashboard, open **Workers & Pages → Create → Pages → Connect to Git**, pick the repository, and set the build command to `npm run build` and the output directory to `dist` (the same folder as `pages_build_output_dir` in `wrangler.toml`).

    The included `wrangler.toml` already sets `NODE_VERSION = "22.13.1"` under `[vars]`. Cloudflare treats this file as the source of truth for the settings in it and shows them read-only in the dashboard, so change them in the file. Set your address with `baseUrl` in `config.toml`.

    **From your computer:** Taskly doesn't install Wrangler (Cloudflare's command-line tool) for you. Add it once, change `name` in `wrangler.toml` to your project name, then log in, build, and deploy:

    ```bash
    npm install -D wrangler
    npx wrangler login
    npm run build
    npm run deploy:cf
    ```

    `public/_headers` serves the `.md` and `llms` files as text on Cloudflare Pages and Netlify.

  </TabItem>
  <TabItem label="Other hosts">

    Run `npm run build` and upload the **contents** of `dist/` to your web server or static host (GitHub Pages, S3, a shared host, and so on).

    For Apache servers, `public/.htaccess` (copied into `dist/`) sets browser caching for CSS, JavaScript, images, and fonts, and serves the `.md` copies as Markdown with `X-Robots-Tag: noindex`.

  </TabItem>
</Tabs>

<Aside type="note">
  When a host builds from your repository, commit `package-lock.json` and keep
  `dist/`, `.astro/`, and `node_modules/` out of git (the included `.gitignore`
  already does).
</Aside>

### After You Launch
<Steps>

1. Open the live site and click through the main pages, on a computer and on a phone, in every language.
2. Send a test message through the contact form, and sign up for the newsletter once.
3. Test search (⌘K or Ctrl+K).
4. Submit `https://your-domain/sitemap-index.xml` in [Google Search Console](https://search.google.com/search-console) and [Bing Webmaster Tools](https://www.bing.com/webmasters).
5. Optional: set `[seo.ai] indexNowKey`, deploy, and run `npm run indexnow -- --all`. After later deploys, run `npm run indexnow`.
6. Share a link in a chat app to check the preview image.

</Steps>

### Updating The Live Site
With a git-connected host, every push to your main branch deploys automatically. Build locally first (`npm run build`) to catch errors before they reach your visitors.