花拾录
← 返回知识库

改了 .env 后 docker compose restart 不生效:容器还在用旧环境变量

云计算 / 运维导入2026/09/220 阅读0 评论

你修改了项目里的 .env 文件,把数据库连接串、某个开关或 API 地址更新成了新值,然后按肌肉记忆敲下 docker compose restart。容器转了一圈回来了,可程序里读到的还是那个旧值。你反复确认 .env 文件明明已经改了,甚至怀疑自己改错了文件。

现象

典型表现是:程序行为"没变"——连的旧地址、用的旧超时、开关还是老状态。但你去容器里查环境变量:

docker compose exec app env | grep MY_VAR

发现 MY_VAR 果然还是旧值。可 .env 文件里的新值就写在那里。更气人的是,把容器删掉重建,新值立刻就生效了。

根因

关键在于理解环境变量是在什么时候注入容器的。

容器的环境变量,是在创建容器的那一刻(docker create / docker run)由 Docker 写进容器配置里的。这些值会被固化在容器的元数据中,此后容器的进程启动时读到的,就是这份固化下来的快照。

而 docker compose restart 做的事情非常有限:它只是给已经存在的那个容器发一个信号(默认 SIGTERM,等待后 SIGKILL),然后让它重新启动里面的进程。注意——容器本身没有重建,容器配置一个字都没改,那份固化下来的旧环境变量快照原封不动。

于是就成了这样:配置文件变了,容器配置没变,进程读的是容器配置,所以你看到旧值。

有一个很直观的办法可以验证这一点:看容器的创建时间。

docker inspect --format '{{.Created}}' <容器名>

如果这个创建时间比你改 .env 的时间还早,那就说明容器根本没被重建过——它还是当初那个,环境变量自然还是当初那份。restart 不会刷新这个 Created 时间戳,只有重建才会。

up -d 则不同。它会比较"当前 compose 文件 + .env 计算出的目标状态"和"现有容器的实际状态",一旦发现环境变量、镜像、命令、挂载等创建期属性不一致,就会销毁旧容器、按新配置重建一个。重建时环境变量被重新注入,新值自然生效。

一句话总结:restart 重的是进程,up -d 重的是容器。

解决

改了 .env、改了 compose.yaml 里的 environment/env_file 之后,用:

docker compose up -d

它会自己检测到差异并重建需要重建的容器(不需要重建的会原样保留,不会全部推倒)。

改完确认一下注入的值:

docker compose exec app env | grep MY_VAR
docker compose config | grep -A2 MY_VAR   # 看 compose 最终解析出的配置

如果重建后还是旧值,八成是"改错了地方"——比如值定义在 compose.yaml 的 environment: 里,而 environment 的优先级高于 env_file,你只改了 .env 是压不过它的。docker compose config 会把它最终认定的一份配置打印出来,照着看就不会被优先级绕晕。

延伸与预防

把这条规则推广成一句话:任何"在容器创建时注入"的东西,改完都必须重建容器。属于这一类的有:

  • 环境变量(environment / env_file);
  • 端口映射(ports);
  • 卷挂载(volumes);
  • 网络归属(networks);
  • 镜像本身(image);
  • 命令与入口(command / entrypoint)。

反过来,restart 只适合"进程卡死了、想让它重新跑一遍"这种场景。

养成一个习惯:改配置后先问自己"我改的是创建期属性,还是运行时读的文件"。前者要 up -d(重建),后者(比如挂载进去的配置文件)才可能 restart 就够。这个判断能省掉大量"我明明改了为什么没用"的抓狂。

评论(0)

  • 还没有评论,来抢沙发~

相关文章