
如果你使用 DeepSeek、元宝 AI、Copilot、ChatGPT 等 AI 工具,协助配置轩辕镜像、编写 docker pull 命令、修改 Docker Compose 镜像地址、配置镜像加速、排查镜像拉取失败、分析报错日志等问题,请先让 AI 阅读并遵守轩辕镜像的规则文档。
只需在 AI 对话中先发送下面这句话即可:
请先完整阅读并严格遵守以下文档中的全部规则与要求:
https://xuanyuan.cloud/agents.md
在未充分阅读并理解该文档前,不要生成任何命令、配置、修改建议、故障排查方案或技术回答。后续所有输出都必须严格以该文档中的规范为最高优先级执行。查看 agents.md 用法指南与完整示范。国内用户首推 元宝 AI、DeepSeek 的深度思考模式,不推荐豆包 AI;Cursor 等编辑器可在对话 @ 该链接,或加入 User Rules。 若 AI 无法访问外链,可 打开说明文档 复制全文粘贴。文档会随站点更新,复制内容可能过期,建议定期检查。
Admin Username: ***
Admin Password: password
Admin API Key - depends on which image you use
Postgres Username: postgres
Postgres Password: sekret
Postgres Database Name: canvas_development
NOTE: For ARM users (e.g.: Apple Silicon Macs), use the arm variant of the image tags, e.g.: lthub/canvas-dev-only:release.2026-01-28.618.arm
This example expects you to copy the database dump from within the Canvas image in /usr/src/app/dbinit/ to a local directory docker/dev/canvas/. The database dump sql files are executed when postgres starts up with an e***y database to initialize Canvas. Example command to copy the database base to host:
bashdocker create --name tmpCanvas lthub/canvas-dev-only:release.2026-01-28.618 mkdir -p docker/dev/canvas docker cp tmpCanvas:/usr/src/app/dbinit/. docker/dev/canvas/ docker rm -f tmpCanvas
Example docker-compose.yml file below for running this dev instance.
yaml# Only for dev use services: canvas.postgres: image: pgvector/pgvector:pg14 volumes: - canvasPostgresData:/var/lib/postgresql/data - ./docker/dev/canvas:/docker-entrypoint-initdb.d healthcheck: # no built-in healthcheck in the image, so we specify one test: ["CMD-SHELL", "pg_isready -U postgres -h localhost -p 5432"] start_period: 2m # ignore failures in the initial 2 minutes environment: POSTGRES_PASSWORD: sekret canvas: &CANVAS image: lthub/canvas-dev-only:release.2026-01-28.618 ports: - 9100:80 depends_on: canvas.postgres: # if starting from scratch, postgres db needs to populated, which can # take some time, so we need to wait until postgres is healthy condition: service_healthy canvas.redis: condition: service_started volumes: - api_docs:/usr/src/app/public/doc/api - brandable_css_brands:/usr/src/app/app/stylesheets/brandable_css_brands - bundler:/home/docker/.bundle/ - canvas-docker-gems:/home/docker/.gem/ - js-utils_es:/usr/src/app/packages/js-utils/es - js-utils_lib:/usr/src/app/packages/js-utils/lib - js-utils_node_modules:/usr/src/app/packages/js-utils/node_modules - locales:/usr/src/app/config/locales/generated - log:/usr/src/app/log - node_modules:/usr/src/app/node_modules - pacts:/usr/src/app/pacts - public_dist:/usr/src/app/public/dist - reports:/usr/src/app/reports - styleguide:/usr/src/app/app/views/info - tmp:/usr/src/app/tmp - translations:/usr/src/app/public/javascripts/translations - yardoc:/usr/src/app/.yardoc - yarn-cache:/home/docker/.cache/yarn environment: VIRTUAL_HOST: .canvas.docker CANVAS_DATABASE_HOST: canvas.postgres CANVAS_REDIS_HOST: canvas.redis HTTPS_METHOD: noredirect POSTGRES_PASSWORD: sekret ENCRYPTION_KEY: facdd3a131ddd8988b14f6e4e01039c93cfa0160 canvas.job: <<: *CANVAS command: bundle exec script/delayed_job run attach: false # job is very noisy, so we hide logs for it by default ports: [] canvas.redis: image: redis:alpine # Inside the docker network, canvas is served on port 80. When we expose # canvas to external traffic however, we serve canvas on port 9100. This # mismatch means that when app talks directly to canvas using the external # url (http://localhost:9100), it doesn't work because app is also inside # the docker network and needs to use port 80. To workaround this, socat will # redirect internal docker traffic to port 9100 to canvas port 80. canvas.socat: image: alpine/socat:1.8.0.3 depends_on: - canvas command: "TCP-LISTEN:9100,fork,reuseaddr TCP:canvas:80" volumes: canvasPostgresData: {} api_docs: {} brandable_css_brands: {} bundler: {} canvas-docker-gems: {} i18nliner_node_modules: {} js-utils_es: {} js-utils_lib: {} js-utils_node_modules: {} k5uploader_es: {} k5uploader_lib: {} k5uploader_node_modules: {} locales: {} log: {} node_modules: {} pg_data: {} pacts: {} public_dist: {} reports: {} styleguide: {} tmp: {} translations: {} yardoc: {} yarn-cache: {}
To build a new version, first checkout the https://github.com/instructure/canvas-lms repo and switch to a release tag. The release tags are dated, avoid releases that have not yet been deployed and be cautious of the current deployed release. Future release tags might not build properly, and even the currently deployed tags might have bugs that's being patched. For example, if we're currently on 2025-07-02 release tag, both the current 2025-07-02 tags and the future 2025-07-16 tags might have issues, and Instructure is probably still adding commits to them. It might be safer to use the release before 2025-07-02, which is 2025-06-18.
Once you're on a release tag, run the Canvas setup script manually ./script/docker_dev_setup.sh to get a working running Canvas. The Canvas setup script unfortunately does not use Bundler's deployment build option, so it might try to modify the lockfile and run into a permission issue. This is one factor in why it can be difficult to reproduce Canvas builds. See Troubleshooting section for more details.
Once you get docker_dev_setup.sh to complete successfully, then you can build a new image using Canvas' own Dockerfile.production, with the following modifications:
Editing config/redis.yml to replace redis://redis with redis://<%= ENV.fetch('CANVAS_REDIS_HOST', 'redis') %> so the redis host can be configured via env vars.
Create config/environments/development-local.rb with the content below. This file disables Canvas not allowing requests to local IPs as a security measure. Without it, local development has issues (e.g.: course > people ajax loading, grab***g JWKS from a locally running app):
Rails.application.configure do # remove blocking local ips in dev (in particular 10.0.0.1/8, # 192.168.0.0/16), this breaks ajax loading (e.g.: course > people) and makes # Canvas unable to get JWKS from other docker compose services CanvasHttp.blocked_ip_ranges = [] end
Editing config/database.yml to replace <%= ENV.fetch('POSTGRES_PASSWORD') %> with <%= ENV.fetch('POSTGRES_PASSWORD', 'postgres') %>, this fix a build issue that complains POSTGRES_PASSWORD is null.
We want to use the prod Dockerfile so we have the compiled static dependencies and don't need to run a separate webpack container for them.
To build the image, docker-compose.override.yml was edited to add an 'app' section for building the image as follows:
app: build: context: . dockerfile: Dockerfile.production image: lthub/canvas-dev-only:release.2025-04-23.348 environment: VIRTUAL_HOST: .canvas.docker HTTPS_METHOD: noredirect POSTGRES_PASSWORD: sekret ENCRYPTION_KEY: facdd3a131ddd8988b14f6e4e01039c93cfa0160
The image can then be build using docker compose build app and pushed to
Dockerhub using docker compose push app. Remember to update the image tag as
necessary.
Database dump was taken inside the Postgres container and copied to dbinit/
inside the Canvas image. You will need to mount these sql files to your own
Canvas's Postgres init scripts directory in order to properly initialize the
Canvas database. The database dump commands:
pg_dumpall -U postgres -W --globals-only --file=01_globals.sql pg_dump -U postgres -W --create canvas_development > 02_canvas_development.sql pg_dump -U postgres -W --create canvas_test > 03_canvas_test.sql
Edit the 01_globals.sql file to remove the creation of the role postgres as that'll be automatically created by the postgres container.
We won't use the canvas_test database, it's included to make migration to newer Canvas versions easier. Instructure's docker_canvas_setup.sh script's db migration step expects both canvas_development and canvas_test databases to exist.
The Canvas postgres service in your app's docker-compose.yml should look something like this, we need the pgvector plugin, so we're using their image:
canvas.postgres: image: pgvector/pgvector:pg14 volumes: - canvasPostgresData:/var/lib/postgresql/data - ./path/to/canvas/sql:/docker-entrypoint-initdb.d environment: POSTGRES_PASSWORD: sekret
admin@example.com has Integration ID set (so apps requiring PUID don't complain)There is a monthly cron job (1st of every month) that gets executed to rotate the LTI keys, it is configured in: config/initializers/periodic_jobs.rb. The job is Lti::KeyStorage.rotate_keys. We can find the job this creates in the delayed_jobs table:
SELECT * FROM "delayed_jobs" where handler like '%rotate_keys%';
We can change the 'run_at' field so it'll run sooner. Not sure if needed, but best to restart Canvas after changing the db in case that's needed for the change to be picked up. Warning: The rotate keys job enforces 1 hour of separation between rotates, so it'll do nothing if you've already rotated keys once in the past hour. There are 3 old keys to rotate out, so this requires at least 3 hours.
Template:
UPDATE "delayed_jobs" SET run_at = '<INSERT DATETIME>' WHERE id=<INSERT ID>;
Example:
UPDATE "delayed_jobs" SET run_at = '2025-11-17 08:58:00' WHERE id=2793;
File Permission Issue While Building Image
You might get a permission error about writing to the Gemfile.lock or similar. Instructure uses a user/group with a uid/gid of 9999 to run all their containers. So one way we can sidestep the permission issue is to set all Canvas files to be owned by this Instructure docker user/group. In my case, I created a instructure-docker group with a gid of 9999 and set the entire Canvas source directory to be owned by that group.
Alternatively, edit scripts/docker_dev_setup.sh and comment out the CANVAS_SKIP_DOCKER_USERMOD declaration:
#CANVAS_SKIP_DOCKER_USERMOD='true'
This will instead change the Instructor docker user's uid to your own uid.
Unable to migrate to newer database
If the failure is on 20251008075319_add_access_ignite_agent_permission.rb or
similar AI permission migration, then the issue may be the custom roles and
permissions copied over from Canvas prod. Might be a configuration thing, but
the AI migrations seem to be expecting hardcoded roles that doesn't exist for
us, even when creating the Canvas database from scratch.
Edit dbinit/02_canvas_development.sql and delete the section that copies all
the UBC roles and role overrides. The role overrides section should start with
COPY public.role_overrides. The roles section should start with COPY public.roles, as of 2025-10-08, the roles with id 1 to 16 are the default
roles, delete the non-default roles (id 17 and up). Reset the database with the
updated sql file and try the migration again.
Unfortunately, this does require recopying the roles and permissions from prod Canvas again.
您可以使用以下命令拉取该镜像。请将 <标签> 替换为具体的标签版本。如需查看所有可用标签版本,请访问 标签列表页面。
来自真实用户的反馈,见证轩辕镜像的优质服务