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.
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, andcurlon 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:
- Run
just aws-status CONFIGand confirm that CloudFormation is complete, the ECS service has one running task, and the active revision and digest are shown. - Open
http://YOUR_HOSTNAMEand confirm that it redirects to HTTPS. - Open
https://YOUR_HOSTNAME/healthzand confirm that it succeeds. - Sign in with an authorized identity and confirm that an unauthorized identity is rejected by the selected provider.
- Create a document, open it in two browser sessions, edit from both, and confirm that the changes converge.
- Upload an asset and refresh the document.
- Sign in with the Markyard CLI and pull the document.
- 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.