@spree/docs 0.1.129 → 0.1.131
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-reference/store.yaml +468 -249
- package/dist/developer/contributing/developing-spree.md +2 -2
- package/dist/developer/core-concepts/reports.md +1 -1
- package/dist/developer/create-spree-app/quickstart.md +34 -14
- package/dist/developer/dashboard/deployment.md +8 -12
- package/dist/developer/deployment/aws.md +77 -434
- package/dist/developer/deployment/aws_ecs.md +460 -0
- package/dist/developer/deployment/background_jobs.md +106 -0
- package/dist/developer/deployment/caching.md +15 -7
- package/dist/developer/deployment/database.md +4 -4
- package/dist/developer/deployment/docker.md +50 -58
- package/dist/developer/deployment/emails.md +27 -19
- package/dist/developer/deployment/environment_variables.md +33 -21
- package/dist/developer/deployment/quickstart.md +79 -0
- package/dist/developer/deployment/render.md +28 -42
- package/package.json +1 -1
- package/dist/developer/deployment/heroku.md +0 -51
|
@@ -1,466 +1,109 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
3
|
-
|
|
2
|
+
title: AWS — EC2 + RDS
|
|
3
|
+
sidebarTitle: EC2 + RDS
|
|
4
|
+
description: The simplest production Spree on AWS — one EC2 instance and a managed database.
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
The simplest way to run Spree on AWS is the [single-node topology](quickstart.md#single-node-vs-distributed): **one EC2 instance running the Docker image, one RDS database**. No clusters, no load balancers, no task definitions — a complete production store for roughly the cost of a t3.small and a small RDS instance.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
Need auto-scaling, zero-downtime deploys, or CI/CD-driven infrastructure? That's the [ECS Fargate guide](aws_ecs.md) — it runs the same Docker image, so you can start here and graduate later without rework.
|
|
9
10
|
|
|
10
|
-
##
|
|
11
|
+
## What You'll Create
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
| Service | Description |
|
|
15
|
-
|---------|-------------|
|
|
16
|
-
| [AWS ECS Fargate](https://aws.amazon.com/fargate/) | Fully managed container orchestration — run and scale containers without managing infrastructure. |
|
|
17
|
-
| [AWS RDS](https://aws.amazon.com/rds/) | Managed relational database. Spree works with [Aurora PostgreSQL](https://aws.amazon.com/rds/aurora/), [Aurora MySQL](https://aws.amazon.com/rds/aurora/), [RDS PostgreSQL](https://aws.amazon.com/rds/postgresql/), [RDS MySQL](https://aws.amazon.com/rds/mysql/), and [RDS MariaDB](https://aws.amazon.com/rds/mariadb/). |
|
|
18
|
-
| [AWS ElastiCache](https://aws.amazon.com/elasticache/) | Valkey or Redis for background jobs (Sidekiq), [caching](caching.md), and Action Cable. We recommend separate instances for jobs and cache. |
|
|
19
|
-
| [AWS S3](https://aws.amazon.com/s3/) | Object storage for uploaded files (product images, etc.). [More information](assets.md#aws-s3). |
|
|
20
|
-
| [AWS CloudFront](https://aws.amazon.com/cloudfront/) | CDN for asset delivery (images, stylesheets, JavaScript). |
|
|
21
|
-
| [AWS Route 53](https://aws.amazon.com/route53/) | DNS service for domain name management. |
|
|
22
|
-
| [AWS Certificate Manager](https://aws.amazon.com/certificate-manager/) | Free SSL/TLS certificates. Spree requires HTTPS in production. |
|
|
23
|
-
| [AWS ECR](https://aws.amazon.com/ecr/) | Docker container registry for storing your application images. |
|
|
24
|
-
|
|
25
|
-
## Docker Image
|
|
26
|
-
|
|
27
|
-
You can use the [official Docker image](docker.md) (`ghcr.io/spree/spree`) directly, or build your own from your Rails application's Dockerfile.
|
|
28
|
-
|
|
29
|
-
To build and deploy a custom image to AWS ECR via GitHub Actions:
|
|
30
|
-
|
|
31
|
-
```yaml
|
|
32
|
-
name: Deploy to AWS Fargate
|
|
33
|
-
|
|
34
|
-
on:
|
|
35
|
-
push:
|
|
36
|
-
branches: [ main ]
|
|
37
|
-
pull_request:
|
|
38
|
-
branches: [ main ]
|
|
39
|
-
|
|
40
|
-
env:
|
|
41
|
-
AWS_REGION: us-east-1
|
|
42
|
-
ECR_REPOSITORY: spree-starter
|
|
43
|
-
ECS_SERVICE_WEB: spree-web
|
|
44
|
-
ECS_SERVICE_WORKER: spree-worker
|
|
45
|
-
ECS_CLUSTER: spree-cluster
|
|
46
|
-
|
|
47
|
-
jobs:
|
|
48
|
-
build:
|
|
49
|
-
name: Build and Push to ECR
|
|
50
|
-
runs-on: ubuntu-latest
|
|
51
|
-
|
|
52
|
-
outputs:
|
|
53
|
-
image: ${{ steps.build-image.outputs.image }}
|
|
54
|
-
image-tag: ${{ steps.build-image.outputs.image-tag }}
|
|
55
|
-
|
|
56
|
-
steps:
|
|
57
|
-
- name: Checkout code
|
|
58
|
-
uses: actions/checkout@v4
|
|
59
|
-
|
|
60
|
-
- name: Configure AWS credentials
|
|
61
|
-
uses: aws-actions/configure-aws-credentials@v4
|
|
62
|
-
with:
|
|
63
|
-
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
64
|
-
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
65
|
-
aws-region: ${{ env.AWS_REGION }}
|
|
66
|
-
|
|
67
|
-
- name: Login to Amazon ECR
|
|
68
|
-
id: login-ecr
|
|
69
|
-
uses: aws-actions/amazon-ecr-login@v2
|
|
70
|
-
|
|
71
|
-
- name: Build, tag, and push image to Amazon ECR
|
|
72
|
-
id: build-image
|
|
73
|
-
env:
|
|
74
|
-
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
|
|
75
|
-
IMAGE_TAG: ${{ github.sha }}
|
|
76
|
-
run: |
|
|
77
|
-
# Build a docker container and push it to ECR
|
|
78
|
-
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
|
|
79
|
-
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
|
|
80
|
-
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
|
|
81
|
-
echo "image-tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
|
|
82
|
-
|
|
83
|
-
deploy-web:
|
|
84
|
-
name: Deploy Web Service
|
|
85
|
-
runs-on: ubuntu-latest
|
|
86
|
-
needs: build
|
|
87
|
-
if: github.ref == 'refs/heads/main'
|
|
88
|
-
|
|
89
|
-
steps:
|
|
90
|
-
- name: Checkout code
|
|
91
|
-
uses: actions/checkout@v4
|
|
92
|
-
|
|
93
|
-
- name: Configure AWS credentials
|
|
94
|
-
uses: aws-actions/configure-aws-credentials@v4
|
|
95
|
-
with:
|
|
96
|
-
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
97
|
-
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
98
|
-
aws-region: ${{ env.AWS_REGION }}
|
|
99
|
-
|
|
100
|
-
- name: Fill in the new image ID in the Amazon ECS task definition
|
|
101
|
-
id: task-def-web
|
|
102
|
-
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
|
103
|
-
env:
|
|
104
|
-
ECR_REGISTRY: ${{ needs.build.outputs.image }}
|
|
105
|
-
IMAGE_TAG: ${{ needs.build.outputs.image-tag }}
|
|
106
|
-
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
|
|
107
|
-
with:
|
|
108
|
-
task-definition: .aws/web-task-definition.json
|
|
109
|
-
container-name: web
|
|
110
|
-
image: ${{ needs.build.outputs.image }}
|
|
111
|
-
|
|
112
|
-
- name: Deploy Amazon ECS task definition for web
|
|
113
|
-
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
|
114
|
-
with:
|
|
115
|
-
task-definition: ${{ steps.task-def-web.outputs.task-definition }}
|
|
116
|
-
service: ${{ env.ECS_SERVICE_WEB }}
|
|
117
|
-
cluster: ${{ env.ECS_CLUSTER }}
|
|
118
|
-
wait-for-service-stability: true
|
|
119
|
-
|
|
120
|
-
deploy-worker:
|
|
121
|
-
name: Deploy Worker Service
|
|
122
|
-
runs-on: ubuntu-latest
|
|
123
|
-
needs: build
|
|
124
|
-
if: github.ref == 'refs/heads/main'
|
|
125
|
-
|
|
126
|
-
steps:
|
|
127
|
-
- name: Checkout code
|
|
128
|
-
uses: actions/checkout@v4
|
|
129
|
-
|
|
130
|
-
- name: Configure AWS credentials
|
|
131
|
-
uses: aws-actions/configure-aws-credentials@v4
|
|
132
|
-
with:
|
|
133
|
-
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
134
|
-
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
135
|
-
aws-region: ${{ env.AWS_REGION }}
|
|
136
|
-
|
|
137
|
-
- name: Fill in the new image ID in the Amazon ECS task definition
|
|
138
|
-
id: task-def-worker
|
|
139
|
-
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
|
140
|
-
env:
|
|
141
|
-
ECR_REGISTRY: ${{ needs.build.outputs.image }}
|
|
142
|
-
IMAGE_TAG: ${{ needs.build.outputs.image-tag }}
|
|
143
|
-
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
|
|
144
|
-
with:
|
|
145
|
-
task-definition: .aws/worker-task-definition.json
|
|
146
|
-
container-name: worker
|
|
147
|
-
image: ${{ needs.build.outputs.image }}
|
|
148
|
-
|
|
149
|
-
- name: Deploy Amazon ECS task definition for worker
|
|
150
|
-
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
|
151
|
-
with:
|
|
152
|
-
task-definition: ${{ steps.task-def-worker.outputs.task-definition }}
|
|
153
|
-
service: ${{ env.ECS_SERVICE_WORKER }}
|
|
154
|
-
cluster: ${{ env.ECS_CLUSTER }}
|
|
155
|
-
wait-for-service-stability: true
|
|
156
|
-
|
|
157
|
-
migrate:
|
|
158
|
-
name: Run Database Migrations
|
|
159
|
-
runs-on: ubuntu-latest
|
|
160
|
-
needs: [build, deploy-web]
|
|
161
|
-
if: github.ref == 'refs/heads/main'
|
|
162
|
-
|
|
163
|
-
steps:
|
|
164
|
-
- name: Checkout code
|
|
165
|
-
uses: actions/checkout@v4
|
|
166
|
-
|
|
167
|
-
- name: Configure AWS credentials
|
|
168
|
-
uses: aws-actions/configure-aws-credentials@v4
|
|
169
|
-
with:
|
|
170
|
-
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
171
|
-
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
172
|
-
aws-region: ${{ env.AWS_REGION }}
|
|
173
|
-
|
|
174
|
-
- name: Run database migrations
|
|
175
|
-
run: |
|
|
176
|
-
aws ecs run-task \
|
|
177
|
-
--cluster ${{ env.ECS_CLUSTER }} \
|
|
178
|
-
--task-definition spree-web \
|
|
179
|
-
--overrides '{
|
|
180
|
-
"containerOverrides": [{
|
|
181
|
-
"name": "web",
|
|
182
|
-
"command": ["bundle", "exec", "rails", "db:migrate"]
|
|
183
|
-
}]
|
|
184
|
-
}' \
|
|
185
|
-
--launch-type FARGATE \
|
|
186
|
-
--network-configuration '{
|
|
187
|
-
"awsvpcConfiguration": {
|
|
188
|
-
"subnets": ["'${{ secrets.SUBNET_ID_1 }}'", "'${{ secrets.SUBNET_ID_2 }}'"],
|
|
189
|
-
"securityGroups": ["'${{ secrets.SECURITY_GROUP_ID }}'"],
|
|
190
|
-
"assignPublicIp": "ENABLED"
|
|
191
|
-
}
|
|
192
|
-
}'
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
This action requires secrets to be set in your GitHub repository. You can find the full list of secrets in the [AWS ECS Deploy Task Definition](https://github.com/aws-actions/amazon-ecs-deploy-task-definition) GitHub Actions repository.
|
|
196
|
-
|
|
197
|
-
| Secret | Description |
|
|
198
|
-
|--------|-------------|
|
|
199
|
-
| `AWS_ACCESS_KEY_ID` | AWS access key ID |
|
|
200
|
-
| `AWS_SECRET_ACCESS_KEY` | AWS secret access key |
|
|
201
|
-
| `AWS_ACCOUNT_ID` | AWS account ID |
|
|
202
|
-
| `SUBNET_ID_1` | First subnet ID |
|
|
203
|
-
| `SUBNET_ID_2` | Second subnet ID |
|
|
204
|
-
| `SECURITY_GROUP_ID` | Security group ID |
|
|
205
|
-
|
|
206
|
-
## Environment Variables
|
|
207
|
-
|
|
208
|
-
Store secrets in [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) and reference them in your task definitions. Non-sensitive configuration goes in the `environment` array directly.
|
|
209
|
-
|
|
210
|
-
For a full list of available variables, see [Environment Variables](environment_variables.md).
|
|
211
|
-
|
|
212
|
-
### Secrets Manager
|
|
213
|
-
|
|
214
|
-
Create the following secrets in AWS Secrets Manager:
|
|
215
|
-
|
|
216
|
-
| Secret Name | Variable | Description |
|
|
13
|
+
| Resource | Purpose | Starting size |
|
|
217
14
|
|---|---|---|
|
|
218
|
-
|
|
|
219
|
-
|
|
|
220
|
-
|
|
|
221
|
-
|
|
|
222
|
-
|
|
223
|
-
Optional secrets for email delivery, file storage, and error tracking:
|
|
15
|
+
| **EC2 instance** | Runs the Spree container (web + background jobs) | `t3.small` (2 GB) to start, `t3.medium` for comfort |
|
|
16
|
+
| **RDS PostgreSQL** | The only backing service — data, jobs, cache | `db.t4g.small` |
|
|
17
|
+
| **DNS record** | Your store's web address, e.g. `store.example.com`, pointing at the instance's Elastic IP (a free static IP — a plain EC2 IP changes on restart and would break the record) | — |
|
|
18
|
+
| **S3 bucket** (recommended) | Uploaded files (product images) — they shouldn't live on the instance disk. See [Asset Storage](assets.md) | — |
|
|
224
19
|
|
|
225
|
-
|
|
226
|
-
|---|---|---|
|
|
227
|
-
| `spree/smtp-password` | `SMTP_PASSWORD` | SMTP auth password |
|
|
228
|
-
| `spree/sentry-dsn` | `SENTRY_DSN` | Sentry DSN for error tracking |
|
|
20
|
+
## 1. Create the Database (RDS)
|
|
229
21
|
|
|
230
|
-
|
|
22
|
+
Create an **RDS PostgreSQL** instance in the AWS console:
|
|
231
23
|
|
|
232
|
-
|
|
24
|
+
- Same VPC as your EC2 instance will use
|
|
25
|
+
- **Public access: no** — instead, allow inbound port `5432` from the EC2 instance's security group
|
|
26
|
+
- Note the endpoint, username, and password — they become your `DATABASE_URL`
|
|
233
27
|
|
|
234
|
-
|
|
28
|
+
Spree also runs on RDS MySQL/MariaDB and both Aurora flavors — see [Database Configuration](database.md). RDS takes automated daily backups by default, so the database needs no extra care.
|
|
235
29
|
|
|
236
|
-
|
|
30
|
+
## 2. Launch the Instance
|
|
237
31
|
|
|
238
|
-
|
|
239
|
-
{
|
|
240
|
-
"family": "spree-web",
|
|
241
|
-
"networkMode": "awsvpc",
|
|
242
|
-
"requiresCompatibilities": ["FARGATE"],
|
|
243
|
-
"cpu": "1024",
|
|
244
|
-
"memory": "4096",
|
|
245
|
-
"executionRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskExecutionRole",
|
|
246
|
-
"taskRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskRole",
|
|
247
|
-
"containerDefinitions": [
|
|
248
|
-
{
|
|
249
|
-
"name": "web",
|
|
250
|
-
"image": "${ECR_REGISTRY}/${ECR_REPOSITORY}:${IMAGE_TAG}",
|
|
251
|
-
"portMappings": [
|
|
252
|
-
{
|
|
253
|
-
"containerPort": 3000,
|
|
254
|
-
"protocol": "tcp"
|
|
255
|
-
}
|
|
256
|
-
],
|
|
257
|
-
"essential": true,
|
|
258
|
-
"environment": [
|
|
259
|
-
{
|
|
260
|
-
"name": "RAILS_ENV",
|
|
261
|
-
"value": "production"
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
"name": "PORT",
|
|
265
|
-
"value": "3000"
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
"name": "RAILS_MAX_THREADS",
|
|
269
|
-
"value": "3"
|
|
270
|
-
},
|
|
271
|
-
{
|
|
272
|
-
"name": "WEB_CONCURRENCY",
|
|
273
|
-
"value": "auto"
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
"name": "RAILS_LOG_LEVEL",
|
|
277
|
-
"value": "info"
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
"name": "AWS_BUCKET",
|
|
281
|
-
"value": "your-spree-bucket"
|
|
282
|
-
}
|
|
283
|
-
],
|
|
284
|
-
"secrets": [
|
|
285
|
-
{
|
|
286
|
-
"name": "DATABASE_URL",
|
|
287
|
-
"valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/database-url"
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
"name": "REDIS_URL",
|
|
291
|
-
"valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/redis-url"
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
"name": "REDIS_CACHE_URL",
|
|
295
|
-
"valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/redis-cache-url"
|
|
296
|
-
},
|
|
297
|
-
{
|
|
298
|
-
"name": "SECRET_KEY_BASE",
|
|
299
|
-
"valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/secret-key-base"
|
|
300
|
-
}
|
|
301
|
-
],
|
|
302
|
-
"logConfiguration": {
|
|
303
|
-
"logDriver": "awslogs",
|
|
304
|
-
"options": {
|
|
305
|
-
"awslogs-group": "/ecs/spree-web",
|
|
306
|
-
"awslogs-region": "${AWS_REGION}",
|
|
307
|
-
"awslogs-stream-prefix": "ecs"
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
"healthCheck": {
|
|
311
|
-
"command": ["CMD-SHELL", "curl -f http://localhost:3000/up || exit 1"],
|
|
312
|
-
"interval": 30,
|
|
313
|
-
"timeout": 5,
|
|
314
|
-
"retries": 3,
|
|
315
|
-
"startPeriod": 60
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
]
|
|
319
|
-
}
|
|
320
|
-
```
|
|
32
|
+
Launch an EC2 instance (Ubuntu 24.04 or Amazon Linux 2023) and open ports `80` and `443` in its security group. Give it a stable web address: associate an Elastic IP (so the instance's IP survives restarts) and create a DNS record for your domain — e.g. an `A` record for `store.example.com` — pointing at it. HTTPS depends on this: Caddy can only obtain a certificate once the domain resolves to the instance.
|
|
321
33
|
|
|
322
|
-
|
|
34
|
+
Then install Docker:
|
|
323
35
|
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
"family": "spree-worker",
|
|
327
|
-
"networkMode": "awsvpc",
|
|
328
|
-
"requiresCompatibilities": ["FARGATE"],
|
|
329
|
-
"cpu": "512",
|
|
330
|
-
"memory": "2048",
|
|
331
|
-
"executionRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskExecutionRole",
|
|
332
|
-
"taskRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskRole",
|
|
333
|
-
"containerDefinitions": [
|
|
334
|
-
{
|
|
335
|
-
"name": "worker",
|
|
336
|
-
"image": "${ECR_REGISTRY}/${ECR_REPOSITORY}:${IMAGE_TAG}",
|
|
337
|
-
"command": ["bundle", "exec", "sidekiq"],
|
|
338
|
-
"essential": true,
|
|
339
|
-
"environment": [
|
|
340
|
-
{
|
|
341
|
-
"name": "RAILS_ENV",
|
|
342
|
-
"value": "production"
|
|
343
|
-
},
|
|
344
|
-
{
|
|
345
|
-
"name": "RAILS_LOG_LEVEL",
|
|
346
|
-
"value": "info"
|
|
347
|
-
}
|
|
348
|
-
],
|
|
349
|
-
"secrets": [
|
|
350
|
-
{
|
|
351
|
-
"name": "DATABASE_URL",
|
|
352
|
-
"valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/database-url"
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
"name": "REDIS_URL",
|
|
356
|
-
"valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/redis-url"
|
|
357
|
-
},
|
|
358
|
-
{
|
|
359
|
-
"name": "SECRET_KEY_BASE",
|
|
360
|
-
"valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/secret-key-base"
|
|
361
|
-
}
|
|
362
|
-
],
|
|
363
|
-
"logConfiguration": {
|
|
364
|
-
"logDriver": "awslogs",
|
|
365
|
-
"options": {
|
|
366
|
-
"awslogs-group": "/ecs/spree-worker",
|
|
367
|
-
"awslogs-region": "${AWS_REGION}",
|
|
368
|
-
"awslogs-stream-prefix": "ecs"
|
|
369
|
-
}
|
|
370
|
-
},
|
|
371
|
-
"healthCheck": {
|
|
372
|
-
"command": ["CMD-SHELL", "pgrep -f sidekiq || exit 1"],
|
|
373
|
-
"interval": 30,
|
|
374
|
-
"timeout": 5,
|
|
375
|
-
"retries": 3,
|
|
376
|
-
"startPeriod": 60
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
]
|
|
380
|
-
}
|
|
36
|
+
```bash
|
|
37
|
+
curl -fsSL https://get.docker.com | sh
|
|
381
38
|
```
|
|
382
39
|
|
|
383
|
-
##
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
40
|
+
## 3. Run Spree
|
|
41
|
+
|
|
42
|
+
Build [your project's image](docker.md) and push it to a registry the instance can pull from (ECR or any other) — or use the stock `ghcr.io/spree/spree:latest` image to start. Then create a `docker-compose.yml` on the instance:
|
|
43
|
+
|
|
44
|
+
```yaml docker-compose.yml
|
|
45
|
+
services:
|
|
46
|
+
web:
|
|
47
|
+
image: your-registry/my-store:latest # or ghcr.io/spree/spree:latest
|
|
48
|
+
restart: unless-stopped
|
|
49
|
+
environment:
|
|
50
|
+
DATABASE_URL: postgres://spree:YOUR_PASSWORD@your-db.abc123.eu-west-1.rds.amazonaws.com:5432/spree_production
|
|
51
|
+
SECRET_KEY_BASE: generate-with-openssl-rand-hex-64
|
|
52
|
+
RAILS_HOST: store.example.com
|
|
53
|
+
# Uploads to S3 (recommended) — see /developer/deployment/assets
|
|
54
|
+
# AWS_REGION: eu-west-1
|
|
55
|
+
# AWS_BUCKET: my-store-uploads
|
|
56
|
+
# AWS_ACCESS_KEY_ID: ...
|
|
57
|
+
# AWS_SECRET_ACCESS_KEY: ...
|
|
58
|
+
# Email delivery — see /developer/deployment/emails
|
|
59
|
+
# SMTP_HOST: smtp.resend.com
|
|
60
|
+
# SMTP_USERNAME: resend
|
|
61
|
+
# SMTP_PASSWORD: ...
|
|
62
|
+
# SMTP_FROM_ADDRESS: orders@example.com
|
|
63
|
+
|
|
64
|
+
# Caddy terminates HTTPS with an automatic Let's Encrypt certificate
|
|
65
|
+
caddy:
|
|
66
|
+
image: caddy:2
|
|
67
|
+
restart: unless-stopped
|
|
68
|
+
ports:
|
|
69
|
+
- "80:80"
|
|
70
|
+
- "443:443"
|
|
71
|
+
command: caddy reverse-proxy --from store.example.com --to web:3000
|
|
72
|
+
volumes:
|
|
73
|
+
- caddy_data:/data
|
|
74
|
+
|
|
75
|
+
volumes:
|
|
76
|
+
caddy_data:
|
|
77
|
+
```
|
|
398
78
|
|
|
399
79
|
```bash
|
|
400
|
-
|
|
401
|
-
aws application-autoscaling register-scalable-target \
|
|
402
|
-
--service-namespace ecs \
|
|
403
|
-
--resource-id service/spree-cluster/spree-web \
|
|
404
|
-
--scalable-dimension ecs:service:DesiredCount \
|
|
405
|
-
--min-capacity 2 \
|
|
406
|
-
--max-capacity 6
|
|
407
|
-
|
|
408
|
-
# Scale based on average CPU utilization (target 70%)
|
|
409
|
-
aws application-autoscaling put-scaling-policy \
|
|
410
|
-
--service-namespace ecs \
|
|
411
|
-
--resource-id service/spree-cluster/spree-web \
|
|
412
|
-
--scalable-dimension ecs:service:DesiredCount \
|
|
413
|
-
--policy-name spree-web-cpu-scaling \
|
|
414
|
-
--policy-type TargetTrackingScaling \
|
|
415
|
-
--target-tracking-scaling-policy-configuration '{
|
|
416
|
-
"TargetValue": 70.0,
|
|
417
|
-
"PredefinedMetricSpecification": {
|
|
418
|
-
"PredefinedMetricType": "ECSServiceAverageCPUUtilization"
|
|
419
|
-
},
|
|
420
|
-
"ScaleInCooldown": 300,
|
|
421
|
-
"ScaleOutCooldown": 60
|
|
422
|
-
}'
|
|
80
|
+
docker compose up -d
|
|
423
81
|
```
|
|
424
82
|
|
|
425
|
-
|
|
83
|
+
The database is migrated automatically on boot. Once DNS resolves, your store is live at `https://store.example.com` — admin at `/admin`, background jobs running inside the web container ([combined mode](quickstart.md#web-and-worker)).
|
|
426
84
|
|
|
427
|
-
|
|
85
|
+
## Updating
|
|
428
86
|
|
|
429
|
-
|
|
87
|
+
Deploying a new version is a pull and a restart:
|
|
430
88
|
|
|
431
|
-
```
|
|
432
|
-
|
|
89
|
+
```bash
|
|
90
|
+
docker compose pull && docker compose up -d
|
|
433
91
|
```
|
|
434
92
|
|
|
435
|
-
|
|
93
|
+
This restarts the container with a few seconds of downtime — the point at which that matters is a good signal to consider [ECS Fargate](aws_ecs.md) and its rolling deploys.
|
|
436
94
|
|
|
437
|
-
|
|
95
|
+
## Scaling on One Box
|
|
438
96
|
|
|
439
|
-
|
|
97
|
+
Before reaching for a cluster:
|
|
440
98
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
--task-definition spree-web \
|
|
445
|
-
--overrides '{
|
|
446
|
-
"containerOverrides": [{
|
|
447
|
-
"name": "web",
|
|
448
|
-
"command": ["bundle", "exec", "rails", "db:migrate"]
|
|
449
|
-
}]
|
|
450
|
-
}' \
|
|
451
|
-
--launch-type FARGATE \
|
|
452
|
-
--network-configuration '{
|
|
453
|
-
"awsvpcConfiguration": {
|
|
454
|
-
"subnets": ["subnet-xxx", "subnet-yyy"],
|
|
455
|
-
"securityGroups": ["sg-xxx"],
|
|
456
|
-
"assignPublicIp": "DISABLED"
|
|
457
|
-
}
|
|
458
|
-
}'
|
|
459
|
-
```
|
|
99
|
+
- **Bigger instance** — a `t3.medium` or `t3.large` carries substantial traffic; resizing is a stop/start
|
|
100
|
+
- **Split the worker** — add a second service to the compose file with `command: bin/jobs`, and set `SOLID_QUEUE_IN_PUMA: "false"` on `web` ([split mode](quickstart.md#web-and-worker))
|
|
101
|
+
- **Scale the database** — RDS resizes independently, and read replicas are a console click
|
|
460
102
|
|
|
461
103
|
## Next Steps
|
|
462
104
|
|
|
463
|
-
- [Asset Storage](assets.md) —
|
|
464
|
-
- [
|
|
465
|
-
- [Configure
|
|
466
|
-
- [
|
|
105
|
+
- [Asset Storage](assets.md) — store uploads in S3
|
|
106
|
+
- [Set environment variables](environment_variables.md) — SMTP, Sentry, etc.
|
|
107
|
+
- [Configure CDN](cdn.md) — CloudFront in front of your assets
|
|
108
|
+
- [Deploy the storefront](../storefront/nextjs/deployment.md) — the Next.js storefront ships separately, typically to Vercel
|
|
109
|
+
- [ECS Fargate](aws_ecs.md) — when you outgrow one box
|