nesimtunc

nesimtunc

No static assets found on production using Docker Containers

Hi,

elixir -v
Erlang/OTP 23 [erts-11.0.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Elixir 1.10.3 (compiled with Erlang/OTP 22)

And my deps.

mix deps.get             
Resolving Hex dependencies...
Dependency resolution completed:
Unchanged:
  bamboo 1.5.0
  bamboo_smtp 2.1.0
  certifi 2.5.2
  connection 1.0.4
  cowboy 2.8.0
  cowlib 2.9.1
  csv 2.3.1
  db_connection 2.2.2
  decimal 1.8.1
  ecto 3.4.6
  ecto_sql 3.4.5
  file_system 0.2.8
  gen_smtp 0.15.0
  gettext 0.18.1
  hackney 1.16.0
  idna 6.0.1
  jason 1.2.1
  metrics 1.0.1
  mime 1.4.0
  mimerl 1.2.0
  parallel_stream 1.0.6
  parse_trans 3.3.0
  phoenix 1.5.4
  phoenix_ecto 4.1.0
  phoenix_html 2.14.2
  phoenix_live_dashboard 0.2.7
  phoenix_live_reload 1.2.4
  phoenix_live_view 0.14.4
  phoenix_pubsub 2.0.0
  plug 1.10.4
  plug_cowboy 2.3.0
  plug_crypto 1.1.2
  postgrex 0.15.5
  pow 1.0.20
  ranch 1.7.1
  ssl_verify_fun 1.1.6
  telemetry 0.4.2
  telemetry_metrics 0.5.0
  telemetry_poller 0.5.1
  unicode_util_compat 0.5.0

mix.exs:

defmodule MyApp.MixProject do
  use Mix.Project

  def project do
    [
      app: :myapp,
      version: "0.1.0",
      elixir: "~> 1.7",
      elixirc_paths: elixirc_paths(Mix.env()),
      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {MyApp.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.5.3"},
      {:phoenix_pubsub, "~> 2.0"},
      {:phoenix_ecto, "~> 4.1"},
      {:ecto_sql, "~> 3.4"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_dashboard, "~> 0.2.0"},
      {:telemetry_metrics, "~> 0.4"},
      {:telemetry_poller, "~> 0.4"},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.0"},
      {:pow, "~> 1.0.20"},
      {:bamboo, "~> 1.5"},
      {:bamboo_smtp, "~> 2.1.0"},
      {:csv, "~> 2.3.1"}
    ]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    [
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      test: ["ecto.create --quiet", "ecto.migrate", "test"]
    ]
  end
end

config/releases.exs

import Config

database_url =
  System.get_env("DATABASE_URL") ||
    raise """
    environment variable DATABASE_URL is missing.
    For example: ecto://USER:PASS@HOST/DATABASE
    """

secret_key_base =
  System.get_env("SECRET_KEY_BASE") ||
    raise """
    environment variable SECRET_KEY_BASE is missing.
    You can generate one by calling: mix phx.gen.secret
    """

config :logger, :console,
  level: :info,
  format: "$time $metadata[$level] $message\n",
  metadata: [:request_id]

config :myapp, MyApp.Repo,
  # ssl: true,
  url: database_url,
  pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")

config :myapp, MyAppWeb.Endpoint,
  http: [
    port: String.to_integer(System.get_env("PORT") || "4000"),
    transport_options: [socket_opts: [:inet6]]
  ],
  url: [host: System.get_env("HOST"), port: 80],
  server: true,
  cache_static_manifest: "priv/static/cache_manifest.json",
  secret_key_base: secret_key_base,
  check_origin: [System.get_env("HOST")]

This is not my first production release for Phoenix but I upgraded the the project to the versions above.
I followed latest Deployment Documentation: https://hexdocs.pm/phoenix/releases.html#containers

Here’s my webpack.config file

const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = (env, options) => ({
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: false
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  entry: {
    './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js'))
  },
  output: {
    filename: 'app.js',
    path: path.resolve(__dirname, '../priv/static/js')
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader'
      }
    }, {
      test: /\.s?css$/,
      use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
    }]
  },
  plugins: [
    new MiniCssExtractPlugin({ filename: '../css/app.scss' }),
    new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
  ]
});

Here’s my DockerFile

FROM elixir:1.9.4-alpine as build

# install build dependencies

RUN apk add --update git build-base nodejs npm yarn python

# prepare build dir

RUN mkdir /app

WORKDIR /app

# install hex + rebar

RUN mix local.hex --force && \

mix local.rebar --force

# set build ENV

ENV MIX_ENV=prod

# install mix dependencies

COPY mix.exs mix.lock ./

COPY config config

RUN mix do deps.get, deps.compile

# build assets

COPY assets/package.json assets/package-lock.json ./assets/

RUN npm --prefix ./assets ci --progress=false --no-audit --loglevel=error

COPY priv priv

COPY assets assets

RUN npm run --prefix ./assets deploy

RUN mix phx.digest

# compile and build release

COPY lib lib

# uncomment COPY if rel/ exists

# COPY rel rel

RUN mix do compile, release

# prepare release image

FROM alpine:3.9 as app

RUN apk add --update bash openssl

RUN apk add --no-cache ncurses-libs

RUN mkdir /app

WORKDIR /app

COPY entrypoint.sh ./

COPY --from=build /app/_build/prod/rel/myapp ./

RUN chown -R nobody: /app

USER nobody

EXPOSE 4004

ENV HOME=/app

CMD ["bash", "/app/entrypoint.sh"]

and entrypoint.sh file

# File: my_app/entrypoint.sh

#!/bin/bash

# docker entrypoint script.

bin="/app/bin/myapp"

# start the elixir application

exec "$bin" "start"

exec "$bin" "eval MyApp.Release.migrate"

which is create an docker container image and accessible from outside however there’s no static files found. So, the web site is pure html without theme, images etc.

I can’t find what’s wrong?

Marked As Solved

nesimtunc

nesimtunc

no… here’s how I solved, before get rid off the webpack, I gave up sass, so converted everything to css, remove the saas loader and all good.

latest webpack.config.js

const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = (env, options) => ({
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: false,
        parallel: true,
        sourceMap: false
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  entry: {
    './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js'))
  },
  output: {
    filename: 'app.js',
    path: path.resolve(__dirname, '../priv/static/js')
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader'
      }
    }, {
      test: /\.css$/i,
      use: [MiniCssExtractPlugin.loader, 'css-loader']
    }]
  },
  plugins: [
    new MiniCssExtractPlugin({ filename: './css/app.css' }),
    new CopyWebpackPlugin([{ from: 'static/', to: '../' }]),
    new CopyWebpackPlugin([{ from: 'css/', to: '../css/' }]),
  ]
});

Now all good!

P.S.: Next time I’ll avoid to use webpack at all, even though it has great benefits, but just another headache.

Where Next?

Popular in Questions Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
Kagamiiiii
Student & New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
New

Other popular topics Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
William
I would like to know that is there any online source for learning Phoenix Framework for building E-Commerce Store? Any advantage on build...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement