Files
remark42/site/content/docs/getting-started/installation/index.md
T
Dmitry VerkhoturovandGitHub a5b2fe3cfc Consolidate the frontend toolchain onto babel, and ship one bundle (#2178)
Four upgrades that were finished but never merged, the compiler collapse
they enable, and the dependency sweep that follows. Direct
devDependencies go from 78 to 60 and dependencies from 10 to 9.

Three were doing the same job: `ts-loader` stripped types in webpack,
`babel-loader` did everything else, and `@swc/jest` repeated both for the
tests with its own copy of the JSX settings. Babel is the one that
survives, because the `data-testid` stripper has no equivalent elsewhere.

`ts-loader` ran `transpileOnly: true`, so it only stripped types, which
`@babel/preset-typescript` does; `fork-ts-checker-webpack-plugin` was
already what type-checks. Jest runs `babel-jest` against the same
`.babelrc.js` the bundle uses, passed as `configFile` because a
file-relative babel config does not reach the `node_modules` packages in
`transformIgnorePatterns`, and `jest.config.mjs` is plain ESM because a
`.ts` config is compiled against `tsconfig.json`, whose
`verbatimModuleSyntax` rejects ESM syntax in a file the package has not
declared as a module.

That removes `ts-loader`, `@swc/jest` and `@swc/core`. The last was
pinned to 1.2.205 from 2022 with no way forward, because newer builds
emit non-configurable exports and break `jest.spyOn` across 13 suites.

Babel compiles a file at a time with no type information, so it cannot
tell a type-only import from a real one and keeps the module. One line,
`import { boundActions } from './connected-comment'`, pulled the whole
redux store into `last-comments.mjs` and doubled it. `verbatimModuleSyntax`
and `@typescript-eslint/consistent-type-imports` mark them properly; the
statement has to be a separate `import type`, since verbatim semantics
keep an inline `import { type X }` and load the module anyway.

The legacy and modern compilations produced the same bytes. Both read the
same browserslist query, `defaults, not IE 11, not samsung 12` resolves to
chrome 109 and up, and nothing in the source needs transforming for that
set, so 28 of the 29 output pairs were byte-identical.

That made the module/nomodule switch worse than redundant: it served the
`.js` file to browsers with no ES module support, and those files carried
`??`, `?.` and class fields, so the fallback handed its own audience a
syntax error. There is now one bundle, always loaded as a module, in the
five templates and in the seven `site/` documents integrators copy from.
A production build emits 29 files rather than 58, in about 3 seconds
rather than 17. Two of those documents did not work at all beforehand:
the SPA snippet could not parse, and the subdomain example had an
unterminated string.

`@babel/core` 8 declares `^22.18 || >=24.11` and `size-limit` 13 declares
`^22.18 || ^24 || >=26`, so 20 was below the floor of two things installed
here; pnpm only warns, which is why every build passed. All seven places
the frontend pins it move together. `site/` is untouched: it builds with
yarn and eleventy and installs neither.

`eslint --print-config` before and after gives 173 active rules on an
application file against 172, and 172 on a spec file and a plain JS file
against 171. What is gone is three `flowtype` rules with no Flow here,
`no-new-object` and `no-new-symbol` whose upstream replacements are on,
`react/forbid-foreign-prop-types` with no propTypes anywhere, and, on TS
only, `no-useless-constructor`, whose typescript-eslint version is on at
error. `@babel/core` is pinned to 8 across the workspace because
`@jest/transform` and `istanbul-lib-instrument` depend on 7 outright; a
second scoped override holds `eslint-config-preact` on 7, since its
`@babel/eslint-parser` loads babel 7 syntax plugins.

`fast-async` rewrote every async function into nodent promise chains,
calls babel's `transform` synchronously, which babel 8 removed, and every
browser in the target list runs async natively. `prefresh` blew its stack
on `createContext` under babel 8 with no newer release to move to, which
compiled `intl.tsx` and `store/context.tsx` into throwing stubs, so
`pnpm dev:app` could not run the widget at all. `core-js` is not injected
now that `useBuiltIns` is gone, `postcss-custom-properties` was reached
directly although nothing declared it and resolved only through pnpm's
private hoist directory, and `cssnano` ran in both postcss chains although
`CssMinimizerPlugin` already uses it.

`pnpm lint`, `pnpm test` and `pnpm build` now work from `frontend/` as
`CLAUDE.md` and the contributing guide have always said they do; the
workspace root defined none of them.
2026-08-21 19:13:25 -05:00

5.2 KiB
Raw Blame History

title, aliases
title aliases
Installation
/docs/getting-started/
/docs/

System Requirements

Remark42 is designed to be lightweight and efficient. Based on production usage statistics from busy installations:

  • CPU: Minimal usage (typically under 0.1%)
  • Memory: ~80MiB RAM (less than 4% of 2GB)
  • Network: Moderate bandwidth requirements
  • Disk: Small footprint (under 200MB for a 5-year-old installation with regular activity)

These requirements make Remark42 suitable for running on small VPS instances, shared hosting environments, or even on Raspberry Pi and similar devices.

Setup Remark42 Instance on Your Server

Installation in Docker

This is the recommended way to run Remark42

  • copy provided docker-compose.yml and customize for your needs
  • make sure you don't keep ADMIN_PASSWD=something... for any non-development deployments
  • pull prepared images from the Docker Hub and start - docker compose pull && docker compose up -d
  • alternatively, compile from the sources - docker compose build && docker compose up -d

Installation with Binary

  • download archive for the stable release
  • unpack with gunzip (Linux, macOS) or with zip (Windows)
  • run as remark42.{os}-{arch} server {parameters...}, i.e., remark42.linux-amd64 server --secret=12345 --url=http://127.0.0.1:8080
  • alternatively compile from the sources - make OS=[linux|darwin|windows] ARCH=[amd64,386,arm64,arm]. Source binary builds require Go 1.25, Node 24+, PNPM 10, and Perl because the frontend assets are built and embedded locally.

Installation as a systemd Service

For a clean persistent setup without lengthy command line parameters:

  1. Create an environment file /etc/remark42.env:

    SECRET=12345
    REMARK_URL=http://127.0.0.1:8080
    
  2. Create a systemd service file /etc/systemd/system/remark42.service:

    [Unit]
    Description=Remark42 Commenting Server
    After=syslog.target
    After=network.target
    
    [Service]
    Type=simple
    EnvironmentFile=/etc/remark42.env
    ExecStart=/usr/local/bin/remark42 server
    WorkingDirectory=/var/www/remark42       # directory where data files are stored and automatic backups will be created
    Restart=on-failure
    User=nobody                              # another good alternative is `www-data`
    Group=nogroup                            # another good alternative is `www-data`
    
    [Install]
    WantedBy=multi-user.target
    
  3. Enable and start the service:

    sudo systemctl enable remark42.service
    sudo systemctl start remark42.service
    
  4. To update configuration, edit the environment file and restart the service:

    sudo systemctl restart remark42.service
    

Setup on Your Website

Add config for Remark on a page of your site (here is the full reference):

  • REMARK_URL the URL where is Remark42 instance is served, passed as REMARK_URL to backend
  • YOUR_SITE_ID - the SITE that you passed to Remark42 instance on start, remark by default.
<script>
	var remark_config = {
		host: "REMARK_URL",
		site_id: "YOUR_SITE_ID",
	}
</script>

For example:

<script>
	var remark_config = {
		host: "https://demo.remark42.com",
		site_id: "remark",
	}
</script>

After that place the code snippet right after config.

<script>!function(e,n){for(var o=0;o<e.length;o++){var r=n.createElement("script"),d=n.head||n.body;r.type="module",r.async=!0,r.defer=!0,r.src=remark_config.host+"/web/"+e[o]+".mjs",d.appendChild(r)}}(remark_config.components||["embed"],document);</script>

{{< note "💡" >}} Note: You can place the config with the snippet in any place of the HTML code of your site. If it is closer to start of the HTML (for example in <head>) it will start loading sooner and show comments faster. {{< /note >}}

Put the next code snippet on a page of your site where you want to have comments:

<div id="remark42">Comments loading...</div>

After that widget will be rendered inside this node. Any content you place inside the div (such as "Comments loading..." above) is automatically removed once the widget initialises, so you can use it as a loading placeholder.

For more information about frontend configuration please learn about other parameters here If you want to set this up on a Single Page App, see the appropriate doc page.

Quick installation test

To verify if Remark42 has been properly installed, check a demo page at ${REMARK_URL}/web URL. Make sure to include remark site ID to the ${SITE} list.

Build from the source

  • to build Docker container - make docker. This command will produce container ghcr.io/umputun/remark42
  • to build a single binary for direct execution - make OS=<linux|windows|darwin> ARCH=<amd64|386>. This requires Go 1.25, Node 24+, PNPM 10, and Perl, builds frontend assets locally, and produces an executable remark42 file with everything embedded