Self-hosting

Deploy Markyard in your AWS account

This guide is for customer administrators deploying a new production Markyard hosting with the supplied AWS CDK deployment package.

Customer guideIdentity administrator

The deployment is designed for a fresh installation in one customer-operated AWS account. It does not import an existing non-AWS installation. Each hosting uses the one identity provider named in your Markyard implementation plan.

What the deployment creates

The deployment creates two protected AWS CloudFormation stacks:

  • a foundation containing the network, encryption key, DynamoDB table, S3 bucket, container repository, and application permissions; and
  • a service containing the private application task, public HTTPS load balancer, certificate, DNS record, application logs, and alarms.

The application runs as one task so that a document has one live collaboration owner. An upgrade therefore causes a brief interruption while browsers and command-line clients reconnect. Durable documents and assets remain in DynamoDB and S3.

Before you begin

Arrange the following before the deployment window:

  • a dedicated or approved AWS account and Region;
  • an AWS CLI v2 profile or AWS IAM Identity Center profile for that account and Region;
  • permission to bootstrap and deploy AWS CDK applications, including CloudFormation, IAM, VPC, KMS, DynamoDB, S3, ECR, ECS, Elastic Load Balancing, ACM, Route 53, CloudWatch, SNS, WAF, Secrets Manager, and Systems Manager permissions as applicable;
  • a public Route 53 hosted zone in the target account and the desired application hostname within that zone;
  • the Markyard deployment checkout supplied to your organization, on a clean committed revision;
  • Node.js 22, AWS CLI v2, Docker, Dagger 0.21.7, just, Git, and curl on the deployment workstation; and
  • outbound internet access from the workstation for dependency, container, and test downloads.

AWS requires every target account/Region environment to be bootstrapped before CDK can deploy stacks. The Markyard bootstrap command performs that step and enables termination protection. See AWS CDK bootstrapping for the AWS-managed resources and roles involved.

Check service quotas before the window. The default installation needs capacity for two Availability Zones, one public Application Load Balancer, one public-IP Fargate task, task network interfaces, one DynamoDB table, and one S3 bucket.

1. Reserve the domain

Choose a hostname such as markyard.example.com. Confirm that:

  • the public Route 53 hosted zone already exists;
  • the zone name is the hostname's parent, for example example.com;
  • no conflicting DNS record uses the application hostname; and
  • your DNS and certificate policies permit CDK to create the application alias and ACM validation records.

Record the hosted zone ID, hosted zone name, and application hostname. The first service deployment can take longer while ACM validates the certificate.

2. Configure the selected identity provider

Use only the identity provider specified for this hosting:

The provider application must be a confidential Web application. Register the callback and logout URLs shown in its guide exactly; do not use wildcards.

After the provider application is ready, create the corresponding AWS application secret. Follow only the page for the provider selected for this hosting:

The secret must be in the deployment account and Region. Keep its complete ARN, including the six-character suffix that AWS adds to the secret name.

3. Prepare optional integrations

Create these resources before deployment only when your organization uses them:

Integration Preparation
Alarm notifications Create an SNS topic in the deployment account and Region, subscribe the operations team, and record its ARN.
Web application firewall Create a regional WAFv2 web ACL in the deployment Region and record its ARN. The deployment associates it but does not create managed WAF rules.
Sentry Create separate web, server, and CLI projects in the same Sentry organization. Record their public DSNs, environment name, and desired trace sample rate from 0 to 1. Artifact uploads remain a separate manual release operation.
Customer-managed secret encryption Create or select a KMS key in the deployment account and Region, allow the deployment roles to use it, use it for the application secret, and record its key ARN.

Omit an integration from the configuration when it is not used. In particular, do not insert empty ARN or DSN strings.

4. Create the deployment configuration

Make a private working copy of the configuration template included with the deployment package. In the commands below, CONFIG means the filename of that private copy. Do not commit the completed file if it contains customer account or integration details.

A minimal configuration has this shape:

{
  "schemaVersion": 1,
  "stage": "production",
  "account": "123456789012",
  "region": "eu-central-1",
  "awsProfile": "markyard-production",
  "imageDigest": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
  "domain": {
    "hostname": "markyard.example.com",
    "hostedZoneId": "Z0123456789EXAMPLE",
    "hostedZoneName": "example.com"
  },
  "auth": {
    "providerId": "selected-provider",
    "applicationSecretArn": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:markyard-production-AbCd12"
  },
  "storage": {
    "deploymentKey": "customer-production"
  }
}

Replace every example value. Use the exact provider ID specified in the selected secret guide. The all-zero image digest is only an initial placeholder; the deployment workflow builds, tests, publishes, resolves, and deploys the real immutable digest.

Choose storage.deploymentKey once and keep it unchanged for the lifetime of the installation. It identifies the durable object namespace. Changing it makes existing objects appear to be missing.

Required configuration

Property Meaning
schemaVersion Must be 1.
stage Stable lowercase name used in stack and resource names, normally production.
account Twelve-digit AWS account ID.
region AWS Region used by the profile and all regional integrations.
awsProfile Local AWS CLI or IAM Identity Center profile name.
imageDigest Initial placeholder or the currently recorded SHA-256 image digest. Deployment and rollback commands resolve it safely.
domain.hostname Public application hostname.
domain.hostedZoneId Existing public Route 53 hosted zone ID.
domain.hostedZoneName Existing public hosted zone name.
auth.providerId Provider ID from the selected identity guide; it must match the provider object in the secret.
auth.applicationSecretArn Complete Secrets Manager secret ARN in the configured account and Region.
storage.deploymentKey Stable installation identifier that must never be changed after first deployment.

Optional configuration

Property Default Meaning
auth.applicationSecretKmsKeyArn AWS-managed secret encryption Customer-managed KMS key used by the application secret.
compute.cpu 1024 Fargate CPU units; 1024 is one vCPU.
compute.memoryMiB 2048 Fargate memory in MiB. It must be valid for the selected CPU value.
observability.logRetentionDays 30 CloudWatch application-log retention using a supported CloudWatch retention value.
observability.alarmTopicArn none Existing SNS topic receiving deployment alarms.
observability.sentryWebDsn none Public DSN for the Sentry web project. Set all three Sentry DSNs together.
observability.sentryServerDsn none Public DSN for the Sentry server project. Set all three Sentry DSNs together.
observability.sentryCliDsn none Public DSN for the Sentry CLI project. Set all three Sentry DSNs together.
observability.sentryEnvironment production Sentry environment label.
observability.sentryTracesSampleRate 0.025 Fraction of transactions traced, from 0 through 1.
security.webAclArn none Existing regional WAFv2 web ACL associated with the load balancer.
runtime.rustLog production information level Application log filter. Change it temporarily and cautiously because verbose logging increases cost.

AWS permits only specific Fargate CPU and memory combinations. The deployment rejects an invalid pair before changing AWS. Start with the default unless load testing demonstrates a need for another size.

Runtime safety and asset controls

The template also exposes the following advanced controls under runtime. Leave them at their supplied values unless Markyard Support recommends a change:

Group Properties
Mutation safety diffTimeoutMs, maxSpliceOps, maxUpdateBytes, maxTombstoneUtf16, maxReplacedFraction, minFractionCheckUtf16, maxReplacedUtf16, maxDocumentUtf16, maxAnchors, anchorContextUtf16
Assets maxSourceBytes, maxSourcePixels, maxPerDocument, maxEncodedBytesPerDocument, processors, processorQueue, processingTimeoutSeconds
Durable mutation recovery storageJournalLeaseMs

Unknown properties, local service endpoints, development authentication, invalid domains, invalid ARNs, invalid rates, and invalid compute sizes are rejected during validation.

5. Authenticate and verify the AWS target

Authenticate the configured AWS profile, then verify its identity:

aws sts get-caller-identity --profile markyard-production
aws configure get region --profile markyard-production

The account and Region must exactly match CONFIG. Do not bypass this safeguard. If your organization uses short-lived IAM Identity Center sessions, ensure the session will remain valid for the complete build and first deployment.

6. Bootstrap and review

Run the commands from the top level of the clean deployment checkout:

just aws-bootstrap CONFIG
just aws-diff CONFIG

Bootstrap is idempotent. Re-running it upgrades the standard CDK bootstrap stack when required. The diff command validates the configuration and application secret, runs the infrastructure checks, and shows proposed changes without deploying them.

Review the diff for the expected account, Region, hostname, provider, and optional integrations. Escalate unexpected replacements of the KMS key, table, bucket, certificate, or DNS record before proceeding.

7. Deploy

Start the deployment only from a clean committed revision:

just deploy-aws CONFIG

The workflow deploys the foundation first. It then builds one revision-labelled container, runs the complete verification suite against that exact container, publishes it to the protected repository, and deploys the service by immutable image digest. If verification or publication fails, an existing service is not changed.

Approve IAM broadening only after comparing it with the reviewed diff. Do not interrupt CloudFormation while a stack update is in progress. If the first service deployment fails, leave the successful foundation stack in place, correct the cause, and run the deployment again.

8. Validate the installation

After deployment completes:

  1. Run just aws-status CONFIG and confirm that CloudFormation is complete, the ECS service has one running task, and the active revision and digest are shown.
  2. Open http://YOUR_HOSTNAME and confirm that it redirects to HTTPS.
  3. Open https://YOUR_HOSTNAME/healthz and confirm that it succeeds.
  4. Sign in with an authorized identity and confirm that an unauthorized identity is rejected by the selected provider.
  5. Create a document, open it in two browser sessions, edit from both, and confirm that the changes converge.
  6. Upload an asset and refresh the document.
  7. Sign in with the Markyard CLI and pull the document.
  8. Confirm that the CloudWatch alarms are healthy and, when configured, reach the SNS subscription.

Record the successful revision and image digest in your change record.

Upgrade Markyard

Use the same account, Region, hostname, provider, secret, and deployment key for every upgrade. Obtain the approved Markyard revision, commit any approved customer configuration change, and run:

just aws-diff CONFIG
just deploy-aws CONFIG
just aws-status CONFIG

Expect a brief disconnect while the single task is replaced. The service drains work, asks browser sessions to reconnect, and uses the ECS deployment circuit breaker to return to the previous task definition when the replacement cannot become healthy.

Repeat the health, sign-in, collaborative editing, asset, and CLI checks after every upgrade. Normal service upgrades must not replace durable storage, DNS, or the certificate.

Roll back an application version

Choose a known-good SHA-256 digest retained in the application ECR repository, obtain change approval, and run:

just rollback-aws CONFIG sha256:KNOWN_GOOD_DIGEST
just aws-status CONFIG

Rollback verifies that the digest exists, changes only the service stack, waits for health, and repeats the deployment smoke checks. The repository retains the latest 20 revision-tagged images. A rollback does not reverse document or asset data.

Operations, retention, and cost

  • Review just aws-status CONFIG, CloudFormation events, ECS service events, CloudWatch alarms, and the application log during incidents.
  • DynamoDB point-in-time recovery, DynamoDB deletion protection, S3 versioning, KMS key rotation, load-balancer deletion protection, and stack termination protection are enabled. S3, DynamoDB, KMS, and logs use retention policies.
  • Retention prevents accidental data loss but is not a complete disaster-recovery plan. Define recovery objectives, access controls, audit review, and any independent export or backup process required by your organization.
  • The main steady costs are one Fargate task, one Application Load Balancer, CloudWatch logs, and usage-based DynamoDB, S3, KMS, Route 53, data transfer, Sentry, SNS, and WAF charges. Load-balancer hourly charges apply even when usage is low.
  • Load-balancer access logs are disabled because share and WebSocket URLs may contain sensitive query tokens. Application logs remain available in CloudWatch.
  • Do not remove stacks as a troubleshooting step. Protected and retained resources can continue to incur charges after a failed or partial removal. Contact Markyard Support for an approved decommissioning plan.

Use the AWS Pricing Calculator with the selected Region and your expected traffic and storage before approving production spend.

Troubleshooting

The account or Region check fails

Re-authenticate the named profile and compare aws sts get-caller-identity and the profile Region with CONFIG. Correct the profile or configuration; do not weaken the check.

Secret validation fails

Confirm that an Entra secret contains exactly authCookieKey, providerTokenKeysJson, and oidcProvidersJson, that the cookie key was generated as 64 random bytes in unpadded base64url form, and that the one provider object's id matches auth.providerId. The validation command never prints secret values.

Certificate validation remains pending

Confirm that the hosted zone ID and name own the configured hostname and that CDK can create DNS validation records. Check for conflicting records and restrictive certificate issuance policies.

The service has no healthy task

Run just aws-status CONFIG, then inspect ECS stopped-task reasons, ECS service events, the target group health reason, and the CloudWatch application log. Correct the first startup error before retrying.

Sign-in fails after the service becomes healthy

Compare the exact HTTPS callback and logout URLs with the selected identity-provider guide. Confirm that the provider ID and secret agree, the client secret is current, and the user is assigned through the provider's access policy.

Documents or assets appear missing

Stop the deployment investigation and verify that the account, Region, stack names, and stable deployment key are unchanged. Do not recreate the bucket or table.

An upgrade fails

Check status and ECS deployment events to confirm whether the circuit breaker restored the previous task. If necessary, use the approved explicit rollback command with the last known-good digest.

AWS references