Files
my-protfolio/DEPLOYMENT.md

3.2 KiB

Deployment

Install or update

On a server with Docker Engine and Compose v2, clone/pull the repository, copy .env.example to .env, set SITE_URL, and run:

docker compose up --build -d

Create the owner separately when needed:

docker compose exec portfolio-app node maintenance.mjs create-user

create-owner and reset-password remain available. Startup never provisions an account. See SELF_HOSTING.md for private imports, persistent volumes, optional Caddy integration, backups, and the one-time upgrade steps.

Gitea Actions

Keep the existing trusted portfolio-ci runner with Docker/Compose, Bash, Git, and OpenSSH. No dedicated Buildx builder, shared image daemon, release manifests, or release-directory symlinks are required. The deployment server needs Git, Bash, flock, Docker/Compose, access to pull this repository, and a checkout on main at DEPLOY_PATH. Configure its .env once, just as for manual deployment.

Keep these repository Actions secrets:

Secret Value
DEPLOY_HOST Server hostname/IP
DEPLOY_USER SSH user allowed to run Docker
DEPLOY_PORT Optional SSH port, default 22
DEPLOY_PATH Absolute server checkout directory
DEPLOY_SSH_KEY Deployment SSH private key
DEPLOY_KNOWN_HOSTS Verified server SSH host-key entry

The server's checkout needs its own Git read access; the deployment SSH key is only for reaching the server. Protect the trusted runner from untrusted fork code. Fork pull requests do not run this workflow.

  • Pull request or manual workflow: build the production image, including TypeScript validation. No browser tests, unit suites, lint, or audit jobs.
  • Push to main: SSH to the server, serialize deployments, fast-forward to the triggering commit, back up an existing running installation, then run docker compose up --build -d --remove-orphans --wait --wait-timeout 60. An obsolete queued commit is skipped. The image is tagged with its commit SHA.

Each deployment builds once on the server, where Docker retains its dependency, native-library, and Next.js caches. NPM_REGISTRY in the server .env selects a mirror if needed. The optional Gitea variable of the same name configures PR builds. Avoid pruning build caches between deployments.

Timing and recovery

The target is under 180 seconds for cached UI-change deployments. Cold builds, network transfers, native-library upgrades, and large backups may take longer. This change does not benchmark or enforce a wall-clock guarantee. Job timeouts allow a first build to finish rather than killing it at the routine-update target.

Backups are retained under backups/before-<commit>-<timestamp> in the server checkout and are ignored by Git. They contain the database, uploads, and generated secret; preserve an externally configured secret separately. Copy backups to separate storage through your normal backup process.

If startup fails after a schema migration, do not simply downgrade the image. Restore the matching pre-upgrade backup and older image using the recovery instructions in SELF_HOSTING.md. A build failure leaves the running container intact; the checkout remains at the attempted commit so the deployment can be retried.