# Ftp

> Deploys static site build output to GoDaddy shared hosting via FTP using basic-ftp in Node.js and SamKirkland/FTP-Deploy-Action in CI. Use when: modifying deploy.js, troubleshooting FTP upload failures, changing remote directory structure, configuring FTP credentials in .env or GitHub Secrets, editing deploy workflows, or adding pre/post-deploy steps.

- Skill: `aibot88/ftp` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add aibot88/ftp`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aibot88/ftp/raw
- Safety review: pending (external: skill-scanner PASS, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: aibot88 (https://skillmd.com/u/aibot88)
- Updated: 2026-08-19
- Page: https://skillmd.com/skills/aibot88/ftp

---


# FTP Deployment

This project deploys a static Eleventy site to GoDaddy shared hosting via FTP. Two deployment paths exist:

- **Local:** `npm run deploy` runs `deploy.js` using `basic-ftp` with credentials from `.env`
- **CI/CD:** GitHub Actions workflows use `SamKirkland/FTP-Deploy-Action@v4.3.5` with credentials from GitHub Secrets

Both paths upload `_site/` to `/public_html/` on the remote server.

## Quick Start

### Local Deploy

```bash
# 1. Copy and fill in credentials
cp .env.example .env

# 2. Build and deploy
npm run deploy
```

### CI Deploy

Push to `main` triggers `.github/workflows/deploy.yml` automatically. No manual step needed.

## Key Concepts

| Concept | Detail |
|---------|--------|
| Library | `basic-ftp` v5.x (local), `SamKirkland/FTP-Deploy-Action` v4.3.5 (CI) |
| Source dir | `_site/` (Eleventy + Tailwind build output) |
| Remote dir | `/public_html/` (GoDaddy default) |
| Connection | FTPS (`secure: true`) |
| Credentials | `.env` locally, GitHub Secrets in CI |
| Build order | Eleventy first, then Tailwind CSS, then deploy |

## Deploy Script (deploy.js)

```javascript
const client = new ftp.Client();
client.ftp.verbose = true;

await client.access({
  host: process.env.FTP_HOST,
  user: process.env.FTP_USER,
  password: process.env.FTP_PASSWORD,
  secure: true
});

const localDir = path.join(__dirname, '_site');
const remoteDir = process.env.FTP_REMOTE_DIR || '/public_html';

await client.ensureDir(remoteDir);
await client.clearWorkingDir();
await client.uploadFromDir(localDir);
```

## WARNING: clearWorkingDir Deletes Everything

`deploy.js` calls `client.clearWorkingDir()` before uploading. This **deletes all files** in the remote directory before uploading the new build. Any files on the server not generated by the build (e.g., uploaded PDFs, .htaccess customizations) will be permanently lost.

If the server has non-build files that must persist, remove `clearWorkingDir()` and use `uploadFromDir` with `overwrite` mode instead.

## Environment Variables

| Variable | Required | Default | Used By |
|----------|----------|---------|---------|
| `FTP_HOST` | Yes | - | deploy.js, GitHub Actions |
| `FTP_USER` | Yes | - | deploy.js, GitHub Actions |
| `FTP_PASSWORD` | Yes | - | deploy.js, GitHub Actions |
| `FTP_REMOTE_DIR` | No | `/public_html` | deploy.js only |

## See Also

- [patterns](references/patterns.md) - Connection handling, error patterns, credential management
- [workflows](references/workflows.md) - Deploy checklists, CI pipeline, troubleshooting

## Related Skills

- See the **github-actions** skill for CI/CD workflow configuration
- See the **eleventy** skill for the build that produces `_site/`

