- Shell 100%
| env | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| CHECKLIST.md | ||
| compose.yaml | ||
| deploy.sh | ||
| README.md | ||
| RESOURCE_BUDGET.md | ||
| VPS_MIGRATION.md | ||
Inshopa deployment
Current direction, 2026-11-04: GitHub source and Actions image builds, an
owner-operated VPS registry, and manual coordinated releases via deploy.sh.
PostgreSQL/S3 stay on Hamravesh. This supersedes the previous Hamgit/Hamdocker
release instructions below; replacement registry commands await provisioning.
See the current decision.
No live migration or CI/registry setup has been performed.
The optional VPS migration notes retain the long-term planning outside the task backlog. Migration may happen in the distant future or never; the current site remains on Hamravesh.
See the ordered deployment checklist (Persian) for preparation, provider-dependent steps and first cutover. Unchecked items are not delivered work.
Independent, local deployment repository for the existing backend, web and gateway images. No application builds, source copies or credentials belong here. Dockerfiles stay in their application repositories; Hamravesh's existing build pipeline remains unchanged. No remote repository or live deployment has been created.
Scope and prerequisites
This is the application layer, not a complete VPS bootstrap. Requires Linux
Docker Engine and Compose 2.30+ (env_file.format: raw). Host TLS ingress,
PostgreSQL and private S3 storage must already be provisioned and reachable.
There is no database container, database provisioning or certificate
automation in this initial repository. No data is copied from the current deployment.
The current gateway image routes inshopa.ir, api.inshopa.ir, www.inshopa.ir
and one-level shop subdomains. It listens on HTTP and assumes upstream HTTPS.
Compose publishes it only on 127.0.0.1:18080, for a TLS proxy on the same host.
The proxy must preserve Host, enforce public HTTPS and have certificate coverage
for the apex and shop subdomains. Do not expose this HTTP port publicly as a shortcut.
Backend and web ports are not published. Their bridge traffic remains HTTP, matching
the existing gateway design; this is not end-to-end encryption.
Web calls a verified HTTPS API origin. Before starting, ensure that origin routes to this deployment, not accidentally to the old live backend. Container access to that origin (including DNS/hairpin routing) must be checked on the chosen host. No custom customer-domain activation or wildcard certificate workflow is added here.
The backend requires PostgreSQL sslmode=verify-full by default. Its narrow private
Hamravesh exceptions do not authorize plaintext to a new Compose database hostname.
Use a reachable TLS-enabled database with the existing restricted application role.
Private cluster DNS names from Darkube will not automatically work on a separate VPS.
If a private CA is required, add an explicit read-only certificate mount and configure
the documented application/driver CA option; do not disable verification.
serve already starts the durable-job worker. No duplicate worker service is needed.
Starting this stack against a live database also starts real background processing;
do not do that merely to validate configuration. Runtime images/configuration and the
database schema must be compatible before startup.
CI publication handoff
Backend, web and gateway each now have a local .github/workflows/publish-image.yml,
.gitlab-ci.yml, ci/publish-image.sh and ci/README.md. GitHub and Hamgit publish
images only; neither deploys. Setup is opt-in with IMAGE_PUBLISH_ENABLED=true,
registry variables and scoped credentials. Web also requires its public CAPTCHA
build key. The private VPS registry and compatible Hamgit runner still need setup.
Local configuration is consolidated in the workspace-root private .env.production.
It holds shared registry settings plus REGISTRY_IMAGE_BACKEND,
REGISTRY_IMAGE_WEB and REGISTRY_IMAGE_GATEWAY. Store those names in GitHub
organization/Hamgit group variables; each project workflow explicitly maps its
own value to the script's REGISTRY_IMAGE. Workflows do not load the local file.
Per-project .env.ci files are retired. Keep this local reference separate from
the server's deployment .env and application runtime environment files.
CI artifacts provide image-ref.txt and Buildx image-metadata.json containing
containerimage.digest. Use the per-application :latest references in the server's private .env,
then run sh deploy.sh manually after checking compatibility. Both providers can
update latest; no automatic release hook is selected. Domestic fallback additionally needs reachable
base images, package dependencies and a domestic runner; no outage guarantee is made.
Prepare a release (on the intended host)
umask 077
mkdir -p private
cp .env.example .env
cp env/backend.env.example private/backend.env
cp env/web.env.example private/web.env
cp env/migration.env.example private/migration.env
docker login registry.hamdocker.ir --username hayyaun
Enter the registry password at the interactive prompt, never in Git or command-line arguments. Docker credentials belong to the deployment account; use a supported credential helper where available. Do not copy an existing Docker credential file into this repository. Private files must remain mode 600 and the directory mode 700.
The current .env.example selects :latest for all three applications.
After the intended builds finish, inspect them after login if needed:
docker buildx imagetools inspect registry.hamdocker.ir/hayyaun/backend-inshopa-backend:<reviewed-tag>
docker buildx imagetools inspect registry.hamdocker.ir/hayyaun/web-inshopa-web:<reviewed-tag>
docker buildx imagetools inspect registry.hamdocker.ir/hayyaun/gateway-inshopa-gateway:<reviewed-tag>
Confirm the images support the host architecture. Owner decision, 2026-11-05:
manual releases use :latest; the owner checks backend/frontend compatibility.
deploy.sh pulls once at the start, then disables further pulls for migration,
preflight and startup. Wait for the intended builds to finish before deploying.
Digest references remain available for an explicitly pinned release or rollback.
Populate private environment files with the intended environment's configuration. They use raw values: no shell expansion and no surrounding quote syntax. URL-encode password characters inside database URLs. API/web BFF credentials must match. Preserve existing session/encryption keys when moving existing data; generating replacements can invalidate sessions or make encrypted stored credentials unreadable. Migration credentials must never be placed in these permanent runtime files.
The NEXT_PUBLIC_ARCAPTCHA_SITE_KEY is baked into the web image during its own build.
Select an image built with the matching backend site key; setting it here at runtime
will not rebuild the browser bundle. Other listed web settings are runtime settings.
These applications consume environment variables; Docker administrators can inspect
them. Git-ignore is not encryption, and mounted Docker secrets would require explicit
application support for reading secret files.
Deploy with migrations
Populate private/migration.env with the dedicated migration login for the same
database and ATLAS_REVISION_SCHEMA as the runtime backend. Keep this file mode
600. It is loaded only by the temporary migrate service, never by backend/web.
The target must already be reachable from this host; this does not open networking
or change the existing TLS rules. No new migration files are created here.
Run the release entry point on Linux (requires flock, normally from util-linux):
sh deploy.sh
It validates configuration, pulls the release images, runs inshopa migrate apply
in a temporary container using the selected backend image, checks runtime database/S3
connectivity, then updates the application services. A failure stops subsequent steps.
The migration container is removed on completion. Concurrent runs from this checkout
are rejected; use one deployment checkout per environment. Other deployment systems
must also be serialized operationally.
Existing services remain running during migration: pending changes must be compatible with the old release. Failed migration files may leave earlier files applied; inspect status before retrying. A preflight/update failure after migration does not undo schema changes. Never automatically roll schema changes back. Every new migration still needs the repository's separate migration approval before its file is created.
The maintenance service is excluded from ordinary compose up. Use deploy.sh for
releases; direct compose up bypasses the release migration step.
Manual checks and application start
docker compose config --quiet
docker compose pull
docker compose run --rm --no-deps backend deployment-check
Use config --quiet, since full rendered configuration includes credentials.
deployment-check uses the existing read-only database/schema and S3 connectivity
checks, without starting workers or applying migrations. It does not certify all
provider permissions or customer flows. The release script runs this after migrations;
running it manually before migrating may correctly report a schema mismatch.
After the target, ingress, credentials and schema are verified and deployment is intended:
docker compose up -d --wait --wait-timeout 180
docker compose ps
curl --fail https://api.inshopa.ir/health/ready
curl --fail --output /dev/null https://inshopa.ir/
Also check a configured shop hostname and approved application flows. Gateway health
checks Nginx only; web health checks its TCP listener. Backend has no bundled HTTP
probe executable and is only tracked as running by Compose. Therefore --wait is
not full readiness: the explicit API readiness request is required. Unhealthy
status alone does not cause Docker's restart policy to restart a container.
See the 3 GB VPS resource budget for the selected starting limits, runtime memory settings, and the unresolved concurrent-CI capacity boundary.
Resource settings are adjustable per service in .env; these are initial hard
ceilings, not benchmarks or capacity promises. Leave host capacity for Docker,
ingress, the OS and any separately hosted database. Logs rotate locally. Persistent
business data lives in PostgreSQL/S3, not the replaceable application containers.
Update and rollback
Keep a private release record containing all three digests, matching configuration,
schema version and verification results. Review image compatibility, wait for successful publication of the intended
:latest images, then rerun deploy.sh and public readiness checks.
Compose recreation can briefly interrupt requests; this is not a zero-downtime rollout.
Environment-file changes take effect with container recreation, not compose restart.
For an application rollback, restore the previous digest set and matching settings
only if compatible with the current database schema, then repeat the same checks.
Do not assume an old binary accepts a newer schema. Keep schema rollback/recovery a
separate decision. docker compose stop stops this application stack; it does not
back up the database or storage. Backup/recovery provisioning remains separate work.
Verification boundary
Initial verification is static Compose validation with synthetic image references and example environment files, plus checks of service isolation and configuration. Private image pulls, live containers, TLS ingress, database/S3 connectivity, resource capacity and a real rollout are not verified by creating this repository.
References
- Compose services and resource settings
- Runtime environment configuration
- Registry login
- Application contracts: backend
Dockerfile,cmd/inshopa/main.go,docs/DEPLOYMENT_CHECK.md; webdocs/DEPLOYMENT.md; gatewayDEPLOYMENT.md.